Properties#
Module: restalchemy.dm.properties
Properties are the core mechanism used by DM models to define and store field values.
Core classes#
AbstractProperty#
Base abstract interface for all properties:
value(property): get/set the current value.set_value_force(value): assign a value bypassing read-only and ID checks.is_dirty(): check if the value has changed since initialization.is_prefetch()(class method): whether the property participates in prefetching.
Property#
The main implementation for scalar and structured fields.
Constructor:
Property(
property_type,
default=None,
required=False,
read_only=False,
value=None,
mutable=False,
example=None,
)
Key behavior:
property_typemust be an instance oftypes.BaseType.defaultmay be a value or a callable; for callables it is evaluated once.- If
valueis provided, it overridesdefault. - If
mutable=False, the initial value is deep-copied for dirty tracking. is_required()andis_read_only()describe validation rules.- Assigning invalid values raises
TypeErrorfromrestalchemy.common.exceptions.
ID properties are represented by IDProperty, which simply overrides is_id_property().
IDProperty#
Specialization of Property used for ID fields:
is_id_property()returnsTrue.- Combined with
ModelWithID/ModelWithUUIDto identify primary key fields.
PropertyCreator and factories#
PropertyCreator#
PropertyCreator is a lightweight factory that stores how to construct a concrete property instance:
- Holds the property class (
PropertyorIDProperty). - Holds the DM type instance (e.g.
types.String()). - Stores positional and keyword arguments used to build the property.
is_prefetch()reflects theprefetchflag (used with relationships).
Instances of PropertyCreator are what you actually assign to model class attributes.
property()#
The main factory function used in models:
from restalchemy.dm import properties, types
class Foo(models.Model):
value = properties.property(types.Integer(), required=True)
Arguments:
property_type: instance oftypes.BaseType.id_property: ifTrue, usesIDProperty.property_class: custom property class (must inherit fromAbstractProperty).- Other keyword arguments are passed to the property constructor (
default,required,read_only,mutable,example, etc.).
Returns:
- A
PropertyCreatorthat will createProperty/IDPropertyinstances when the model is instantiated.
Convenience factories#
required_property(property_type, *args, **kwargs)— setsrequired=True.readonly_property(property_type, *args, **kwargs)— setsread_only=Trueandrequired=True.
Example:
class User(models.ModelWithUUID):
email = properties.required_property(types.Email())
created_at = properties.readonly_property(types.UTCDateTimeZ(), default=datetime.datetime.now)
Property collections and managers#
PropertyCollection#
A collection of property definitions used by MetaModel.
- Stores a mapping of field names to
PropertyCreator(or nestedPropertyCollection). - Implements mapping protocol (
__getitem__,__iter__,__len__). sort_properties()can be used to sort fields by name (useful in tests).instantiate_property(name, value=None)constructs a concrete property instance.
Model.properties is a PropertyCollection at the class level.
PropertyManager#
A runtime container used at instance level:
- Constructed from a
PropertyCollectionand keyword arguments. - Builds actual property objects (or nested
PropertyManagerfor containers). - Exposes
properties(a read-only mapping of property instances). - Exposes
valueas a dict of raw values (read/write).
Model.pour() uses PropertyManager to build instance state:
If a required property is missing, PropertyManager raises PropertyRequired with the field name.
Containers and nested structures#
container()#
Creates a nested PropertyCollection for grouping related fields:
address_container = properties.container(
city=properties.property(types.String()),
zip_code=properties.property(types.String()),
)
class User(models.ModelWithUUID):
name = properties.property(types.String(), required=True)
address = address_container
At runtime, address becomes a nested PropertyManager, and you can access:
Nested containers are especially useful for complex JSON structures and OpenAPI schemas.
Dirty tracking#
Both Property and Relationship support is_dirty():
Propertycompares the current value to the initial one.Relationshipcompares the current related object to the initial one.
Model.is_dirty() iterates over all properties and returns True if any of them is dirty. This is heavily used by storage layers to decide whether updates are needed.
Best practices#
- Always use DM
types.*rather than raw Python types inside properties. - Mark ID fields with
id_property=Trueor use helper models (ModelWithUUID). - Prefer
required_property()andreadonly_property()for clarity when appropriate. - Use
container()for logically grouped fields or nested JSON data.