Свойства (Properties)#
Модуль: restalchemy.dm.properties
Свойства — это основной механизм, используемый DM-моделями для объявления полей и хранения их значений.
Базовые классы#
AbstractProperty#
Абстрактный базовый интерфейс для всех свойств:
value(property): чтение/запись текущего значения.set_value_force(value): установка значения в обход ограничений read-only и ID.is_dirty(): проверка, изменилось ли значение с момента инициализации.is_prefetch()(classmethod): участвует ли свойство в prefetch-загрузке.
Property#
Основная реализация для скалярных и структурированных полей.
Конструктор:
Property(
property_type,
default=None,
required=False,
read_only=False,
value=None,
mutable=False,
example=None,
)
Ключевое поведение:
property_typeдолжен быть экземпляромtypes.BaseType.defaultможет быть значением или вызываемым объектом; в случае callable вызывается один раз.- Если передан
value, он перекрываетdefault. - Если
mutable=False, начальное значение копируется для корректного отслеживанияis_dirty(). is_required()иis_read_only()описывают правила валидации.- При неверном типе или
Noneдля обязательного поля выбрасываются исключения изrestalchemy.common.exceptions.
ID-свойства представлены классом IDProperty, который переопределяет is_id_property().
IDProperty#
Специализация Property для ID-полей:
is_id_property()возвращаетTrue.- Используется совместно с
ModelWithID/ModelWithUUIDдля идентификации первичного ключа.
PropertyCreator и фабрики#
PropertyCreator#
Лёгкая фабрика, которая хранит способ создания конкретного свойства:
- Класс свойства (
PropertyилиIDProperty). - Экземпляр типа (
types.String(),types.Integer()и т.д.). - Позиционные и именованные аргументы для конструктора.
- Флаг
prefetch(для связей).
Именно объекты PropertyCreator вы присваиваете атрибутам класса модели.
property()#
Основная фабрика, используемая в моделях:
from restalchemy.dm import properties, types
class Foo(models.Model):
value = properties.property(types.Integer(), required=True)
Аргументы:
property_type: экземплярtypes.BaseType.id_property: еслиTrue, используетсяIDProperty.property_class: пользовательский класс свойства (должен наследоваться отAbstractProperty).- Остальные ключевые аргументы передаются в конструктор свойства (
default,required,read_only,mutable,exampleи т.д.).
Возвращает:
PropertyCreator, который при инициализации модели создаёт объектыProperty/IDProperty.
Удобные фабрики#
required_property(property_type, *args, **kwargs)— устанавливаетrequired=True.readonly_property(property_type, *args, **kwargs)— устанавливаетread_only=Trueиrequired=True.
Пример:
class User(models.ModelWithUUID):
email = properties.required_property(types.Email())
created_at = properties.readonly_property(
types.UTCDateTimeZ(),
default=datetime.datetime.now,
)
Коллекции свойств и менеджер#
PropertyCollection#
Коллекция определений свойств, используемая MetaModel.
- Хранит отображение имя →
PropertyCreator(или вложенныйPropertyCollection). - Реализует протокол отображения (
__getitem__,__iter__,__len__). sort_properties()сортирует свойства по имени (полезно для тестов).instantiate_property(name, value=None)создаёт конкретный экземпляр свойства.
На уровне класса Model.properties — это PropertyCollection.
PropertyManager#
Контейнер на уровне экземпляра модели:
- Строится на основе
PropertyCollectionи словаря значений. - Создаёт реальные объекты свойств (или вложенные
PropertyManagerдля контейнеров). - Предоставляет
properties(read-only отображение свойств). - Предоставляет
valueкак словарь "сырых" значений (чтение/запись).
Model.pour() использует PropertyManager для инициализации состояния модели:
Если обязательное свойство отсутствует, PropertyManager выбрасывает PropertyRequired с именем поля.
Контейнеры и вложенные структуры#
container()#
Создаёт вложенный PropertyCollection для группировки связанных полей:
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
Во время выполнения address будет вложенным PropertyManager, и вы можете обращаться к:
Вложенные контейнеры удобны для сложных JSON-структур и OpenAPI-схем.
Отслеживание изменений#
И Property, и Relationship поддерживают is_dirty():
Propertyсравнивает текущее значение с начальным.Relationshipсравнивает текущий связанный объект с исходным.
Model.is_dirty() обходит все свойства и возвращает True, если хотя бы одно "грязное". Это активно используется слоями хранения для решения, нужно ли выполнять обновление.
Рекомендации по использованию#
- Всегда используйте DM-типы (
types.String,types.Integerи т.п.), а не "сырые" Python-типы. - Помечайте ID-поля через
id_property=Trueили используйтеModelWithUUID/ModelWithID. - При возможности используйте
required_property()иreadonly_property()для большей читаемости. - Применяйте
container()для логически сгруппированных полей или вложенных JSON-структур.