Должен ли я создавать объекты mapper или использовать декларативный синтаксис в SQLAlchemy?
Существует два (три, но я не считаю Elixir, поскольку он не "официальный") способ определения постоянного объекта с помощью SQLAlchemy:
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
from sqlalchemy.orm import mapper
metadata = MetaData()
users_table = Table('users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String),
)
class User(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return "<User('%s')>" % (self.name)
mapper(User, users_table) # <Mapper at 0x...; User>
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
def __init__(self, name):
self.name = name
def __repr__(self):
return "<User('%s')>" % (self.name)
Я вижу, что при использовании объектов mapper я полностью отделяю определение ORM от бизнес-логики, при этом используя декларативный синтаксис, всякий раз, когда я изменяю класс бизнес-логики, я могу тут же редактировать класс базы данных (который в идеале следует редактировать немного).
В чем я не совсем уверен, какой подход более удобен для бизнес-приложений?
Мне не удалось найти сопоставление между двумя методами картирования, чтобы решить, какой из них лучше подходит для моего проекта.
Я склоняюсь к использованию "нормального" способа (т.е. не декларативного расширения), поскольку он позволяет мне "скрывать" и не допускать бизнес-представления ко всей логике ORM, но я хотел бы услышать убедительные аргументы для обоих подходы.
Ответы
Ответ 1
"Что я не совсем уверен, какой подход более подходит для бизнес-приложения?"
Невозможно ответить в целом.
Однако рассмотрим это.
Django ORM является строго декларативным - и людям это нравится.
SQLAlchemy выполняет несколько действий, не все из которых относятся ко всем проблемам.
-
SQLAlchemy создает SQL-специфический SQL-код из Python общего назначения. Если вы хотите общаться с SQL или сопоставлять классы Python с существующими таблицами, тогда вам нужно использовать явные сопоставления, потому что ваш фокус находится на SQL, а не на бизнес-объектах и ORM.
-
SQLAlchemy может использовать декларативный стиль (например, Django) для создания всего для вас. Если вы этого хотите, то вы отказываетесь от явного написания определений таблиц и явно возитесь с SQL.
-
Эликсир - это альтернатива спасению вас от поиска SQL.
Основной вопрос: "Вы хотите увидеть и коснуться SQL?"
Если вы считаете, что обращение к SQL делает вещи более "поддерживаемыми", тогда вам нужно использовать явные сопоставления.
Если вы считаете, что скрытие SQL делает вещи более "поддерживаемыми", тогда вы должны использовать декларативный стиль.
-
Если вы думаете, что Эликсир может отклониться от SQLAlchemy или не сможет каким-то образом выполнить обещание, тогда не используйте его.
-
Если вы думаете, что Elixir вам поможет, используйте его.
Ответ 2
В нашей команде мы остановились на декларативном синтаксисе.
Обоснование:
-
metadata
тривиально, если нужно: User.metadata
.
- Ваш класс
User
, благодаря подклассу Base
, имеет хороший ctor, который принимает kwargs для всех полей. Полезно для тестирования и в противном случае. Например: user=User(name='doe', password='42')
. Так что не нужно писать ctor!
- Если вы добавляете атрибут/столбец, вам нужно сделать это только один раз. "Не повторяй себя" - это хороший принцип.
Относительно "сохранения ORM из бизнес-представления": на самом деле ваш класс User
, определенный "нормальным" способом, получает серьезную защиту от обезьян SA, когда функция mapper
имеет свой путь с ней. ИМХО, декларативный путь более честен, потому что он кричит: "этот класс используется в сценариях ORM и может не обрабатываться так же, как вы будете относиться к своим простым объектам, отличным от ORM".
Ответ 3
Я обнаружил, что использование объектов mapper намного проще, чем декларативный синтаксис, если вы используете sqlalchemy-migrate для версии вашей схемы базы данных (и это является обязательным для бизнес-приложения с моей точки зрения). Если вы используете объекты mapper, вы можете просто скопировать/вставить объявления таблиц в версии миграции и использовать простой api для изменения таблиц в базе данных. Декларативный синтаксис делает это сложнее, потому что вы должны отфильтровать все вспомогательные функции из определений классов после их копирования в версию миграции.
Кроме того, мне кажется, что сложные отношения между таблицами более четко выражаются с синтаксисом объектов mapper, но это может быть субъективным.
Ответ 4
на текущий момент (2019), спустя много лет, sqlalchemy v1.3 позволяет использовать гибридный подход с лучшими достижениями обоих миров.
https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/table_config.html#using-a-hybrid-approach-with-table
metadata = MetaData()
users_table = Table('users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String),
)
# possibly in a different file/place the orm-declaration
Base = declarative_base(metadata)
class User(Base):
__table__ = Base.metadata.tables['users']
def __str__():
return "<User('%s')>" % (self.name)