DM + SQL storage how-to#
В этом руководстве показано, как сохранять DM-модели в SQL-базу данных с помощью RESTAlchemy.
Вы научитесь:
- Определять DM-модели с
ModelWithUUIDиSQLStorableMixin. - Настраивать SQL-движок (MySQL или PostgreSQL).
- Выполнять операции CRUD с помощью
.save(),.delete()иModel.objects. - Использовать фильтры для построения запросов.
Примеры основаны на examples/dm_mysql_storage.py и examples/dm_pg_storage.py.
Предварительные условия#
- RESTAlchemy установлен (см.
installation.md). - Запущенная база данных:
- MySQL/MariaDB или
- PostgreSQL.
- Установлен соответствующий Python-драйвер, например:
mysql-connector-pythonдля MySQL.psycopg[binary]для PostgreSQL.- Таблицы созданы согласно описанию моделей (см. раздел про миграции ниже).
1. Определяем DM-модели для SQL#
Минимальный паттерн для модели, работающей с SQL:
- Наследуемся от
models.ModelWithUUID(или другогоModelWithID). - Наследуемся от
orm.SQLStorableMixin. - Задаём
__tablename__— имя таблицы. - Описываем свойства DM и их типы.
Пример (упрощённый 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)
Эти модели используют DM-валидацию и связи, а SQLStorableMixin добавляет методы для сохранения.
2. Настройка SQL-движка#
Используйте restalchemy.storage.sql.engines.engine_factory для создания экземпляра движка.
Пример для MySQL#
from restalchemy.storage.sql import engines
engines.engine_factory.configure_factory(
db_url="mysql://user:password@127.0.0.1:3306/test",
)
Пример для PostgreSQL#
from restalchemy.storage.sql import engines
engines.engine_factory.configure_factory(
db_url="postgresql://postgres:password@127.0.0.1:5432/ra_tests",
)
configure_factory() нужно вызвать один раз при старте приложения. Затем все модели с SQLStorableMixin будут получать движок через engine_factory.get_engine().
Дополнительные параметры:
config— конфигурация движка (размеры пула, таймауты и т.п.).query_cache— включение кэша запросов на уровне сессии.
3. Создание таблиц и миграции#
RESTAlchemy не создаёт таблицы автоматически; вместо этого используются миграции.
Команды миграций описаны в README.rst:
ra-new-migration— создание новых файлов миграций.ra-apply-migration— применение миграций к целевой БД.
В примерах приведены наброски DDL, например в dm_mysql_storage.py:
CREATE TABLE `foos` (
`uuid` CHAR(36) NOT NULL,
`foo_field1` INT NOT NULL,
`foo_field2` VARCHAR(255) NOT NULL,
PRIMARY KEY (`uuid`)
) ENGINE = InnoDB;
CREATE TABLE `bars` (
`uuid` CHAR(36) NOT NULL,
`bar_field1` VARCHAR(10) NOT NULL,
`foo` CHAR(36) NOT NULL,
CONSTRAINT `_idx_foo` FOREIGN KEY (`foo`) REFERENCES `foos`(`uuid`)
) ENGINE = InnoDB;
Вы можете адаптировать эти схемы под свою БД или настроить миграции так, чтобы генерировать аналогичный DDL.
4. Базовые операции CRUD#
После настройки движка и создания таблиц DM-модели можно использовать как постоянные сущности.
Создание и сохранение#
foo1 = FooModel(foo_field1=10)
foo1.save() # INSERT в foos
bar1 = BarModel(bar_field1="test", foo=foo1)
bar1.save() # INSERT в bars
Чтение данных#
# Все bars
all_bars = list(BarModel.objects.get_all())
# Один bar по первичному ключу
same_bar = BarModel.objects.get_one(filters={"uuid": bar1.get_id()})
# Все bars для конкретного FooModel
bars_for_foo = list(BarModel.objects.get_all(filters={"foo": foo1}))
# Преобразование в простой dict
print(bar1.as_plain_dict())
Обновление#
foo2 = FooModel(foo_field1=11, foo_field2="some text")
foo2.save()
# Изменяем поле и сохраняем снова (UPDATE)
foo2.foo_field2 = "updated text"
foo2.save()
Если модель уже сохранена, save() вызывает update(), иначе — insert().
Удаление#
Выполняется DELETE по ID-свойствам модели.
5. Фильтры#
Фильтры определены в restalchemy.dm.filters и могут передаваться в get_all() и get_one().
Простые фильтры#
from restalchemy.dm import filters
# foo_field1 == 10
one = FooModel.objects.get_one(filters={"foo_field1": filters.EQ(10)})
# foo_field1 > 5
greater = list(
FooModel.objects.get_all(filters={"foo_field1": filters.GT(5)})
)
# foo_field1 IN (5, 6)
subset = list(
FooModel.objects.get_all(filters={"foo_field1": filters.In([5, 6])})
)
# foo_field1 NOT IN (1, 2)
not_subset = list(
FooModel.objects.get_all(filters={"foo_field1": filters.NotIn([1, 2])})
)
Сложные выражения#
Можно строить сложные условия через AND и OR:
from restalchemy.dm import filters
# WHERE ((foo_field1 = 1 AND foo_field2 = '2') OR (foo_field2 = '3'))
filter_expr = filters.OR(
filters.AND(
{
"foo_field1": filters.EQ(1),
"foo_field2": filters.EQ("2"),
}
),
filters.AND({"foo_field2": filters.EQ("3")}),
)
foo = FooModel.objects.get_one(filters=filter_expr)
Слой хранения переводит такие структуры в SQL WHERE-условия.
6. Транзакции и явные сессии#
По умолчанию каждая операция использует собственную сессию и транзакцию.
Если нужно сгруппировать несколько операций в одну транзакцию, используйте engine.session_manager().
Пример с engine.session_manager()#
from restalchemy.storage.sql import engines
engine = engines.engine_factory.get_engine()
with engine.session_manager() as session:
foo = FooModel(foo_field1=42)
foo.save(session=session)
bar = BarModel(bar_field1="x", foo=foo)
bar.save(session=session)
# В случае ошибки обе INSERT-операции будут откатаны.
Внутри блока with все операции разделяют одну и ту же сессию и транзакцию.
Также можно передавать уже созданную сессию в .save(), .delete() и методы коллекций через параметр session=.
Резюме#
- Описывайте DM-модели с
ModelWithUUID+SQLStorableMixinи__tablename__. - Настраивайте SQL-движок через
engine_factory.configure_factory(). - Используйте миграции для создания и изменения таблиц.
- Применяйте
.save(),.delete()иModel.objects.get_all()/get_one()для CRUD. - Используйте DM-фильтры для описания условий выборки.
- При необходимости контролируйте транзакции через явные сессии.