Skip to content

Слой хранения (Storage layer)#

Слой хранения в RESTAlchemy отвечает за сохранение DM-моделей в базе данных и их последующее чтение.

Он построен как отдельный слой поверх DM (Data Model) и под API-слоем.


Обзор модулей#

Основные модули, относящиеся к SQL-хранилищу:

  • restalchemy.storage.base
  • Абстрактные интерфейсы для сохраняемых моделей и коллекций.
  • restalchemy.storage.exceptions
  • Исключения уровня хранения.
  • restalchemy.storage.sql.engines
  • Фабрика движков и реализации движков для MySQL/PostgreSQL.
  • restalchemy.storage.sql.sessions
  • Сессии БД, транзакции и кэш запросов на уровне сессии.
  • restalchemy.storage.sql.orm
  • ORM-подобные mixin-ы и коллекции (SQLStorableMixin, ObjectCollection).
  • restalchemy.storage.sql.tables
  • Абстракция таблиц, используемая ORM и диалектами.
  • restalchemy.storage.sql.dialect.*
  • Диалект-специфичные построители запросов для MySQL и PostgreSQL.

Обычно вы взаимодействуете только с:

  • DM-моделями + orm.SQLStorableMixin;
  • engines.engine_factory.configure_factory() для настройки движка;
  • коллекцией Model.objects и методами save()/delete() у экземпляров.

Архитектура на высоком уровне#

1. DM-модель#

Вы описываете модель, наследуясь одновременно от:

  • models.ModelWithUUID (или другого базового Model*), и
  • orm.SQLStorableMixin.

Упрощённый пример (по мотивам examples/dm_mysql_storage.py):

from restalchemy.dm import models, properties, relationships, types
from restalchemy.storage.sql import orm


class FooModel(models.ModelWithUUID, orm.SQLStorableMixin):
    __tablename__ = "foos"
    foo_field1 = properties.property(types.Integer(), required=True)
    foo_field2 = properties.property(types.String(), default="foo_str")


class BarModel(models.ModelWithUUID, orm.SQLStorableMixin):
    __tablename__ = "bars"
    bar_field1 = properties.property(types.String(min_length=1, max_length=10))
    foo = relationships.relationship(FooModel)

2. Движок и сессии#

Модуль restalchemy.storage.sql.engines содержит engine_factory, который управляет SQL-движками.

Типичная конфигурация:

from restalchemy.storage.sql import engines

engines.engine_factory.configure_factory(
    db_url="mysql://user:password@127.0.0.1:3306/mydb",
)

Ключевые идеи:

  • Engine парсит db_url, настраивает пул соединений и диалект.
  • Session (PgSQLSession / MySQLSession) создаётся движком и выполняет запросы.
  • session_manager(engine, session=None) из sessions.py оборачивает работу с сессией в транзакцию.

3. ORM-mixin и коллекции#

restalchemy.storage.sql.orm предоставляет:

  • SQLStorableMixin — mixin для DM-моделей, хранимых в SQL.
  • ObjectCollection — коллекция объектов, доступная как Model.objects.

Ответственность:

  • SQLStorableMixin:
  • Связывает DM-модель с таблицей через __tablename__ и get_table().
  • Реализует insert(), save(), update(), delete().
  • Конвертирует свойства DM в значения, пригодные для хранения, и обратно.

  • ObjectCollection:

  • Предоставляет методы get_all(), get_one(), get_one_or_none(), query(), count().
  • Использует фильтры (restalchemy.dm.filters) для описания WHERE-условий.

4. Диалекты и таблицы#

Модули диалектов (restalchemy.storage.sql.dialect.*) и tables.SQLTable — внутренние помощники, которые:

  • Строят SQL-запросы (SELECT/INSERT/UPDATE/DELETE).
  • Подставляют параметры.
  • Выполняют запросы через сессии.

Как правило, вам не нужно обращаться к ним напрямую — ими управляют модели и коллекции.

5. Что происходит при чтении строки#

  • Модель без предзагруженных связей читается из одной таблицы, и её колонки выбираются под собственными именами. Предзагрузка добавляет JOIN, и тогда каждая колонка выбирается под алиасом — иначе две таблицы не смогут выбрать одноимённые колонки.
  • Хранимый NULL — это значение, которого никто не задал: применяется умолчание свойства, ровно как если бы значение не передали в конструктор.
  • Значение, которое тип построил сам из хранимой формы, — UUID, Boolean, UTC-таймстемп UTCDateTimeZ — повторно этим же типом не проверяется. Все остальные типы проверяются как раньше — и всё, что наследует любой из этих трёх: наследник забирает преобразование, но может иметь собственное правило.
  • Любое чтение приходит в restore_row — и одна модель, и целая страница, — поэтому всё, что модель хочет делать при каждом чтении, место именно там. restore_from_storage — это вход, а не способ изменить чтение: его переопределение на страницу строк не влияет.

Жизненный цикл модели с SQL-хранилищем#

  1. Определение модели
  2. Наследуемся от ModelWithUUID и SQLStorableMixin.
  3. Задаём __tablename__.
  4. Описываем поля и связи через свойства DM.

  5. Настройка движка

  6. Один раз при старте приложения вызываем engine_factory.configure_factory(db_url=...).

  7. Создание таблиц / миграции

  8. Используем инструменты миграций (ra-new-migration, ra-apply-migration) для создания/обновления схемы БД.

  9. CRUD-операции

  10. Создаём экземпляры моделей и вызываем .save().
  11. Используем Model.objects.get_all() / .get_one(filters=...) для чтения.
  12. Вызываем .delete() для удаления.

  13. Фильтры и сложные запросы

  14. Строим фильтры через restalchemy.dm.filters и передаём их в objects.get_all() / objects.get_one().

  15. Транзакции и сессии (по необходимости)

  16. Оборачиваем группы операций в явный session_manager(), когда нужен точный контроль над транзакциями.

Все эти шаги показаны в DM+SQL how-to и в примерах examples/dm_mysql_storage.py и examples/dm_pg_storage.py.