Сравнение экземпляров объектов SQLAlchemy для равноправия атрибутов
Приложение My Flask-Restful имеет несколько "объектов". В первой версии приложения это простые структуры данных
без каких-либо действий, реализованных как диктовки или списки диктов.
Атрибуты этих "объектов" могут измениться. Я использую функцию генератора для отслеживания изменений, а затем предупреждаю веб-клиентов через сервер-отправленные события (SSE).
Это работает, поддерживая "старую" копию объекта, подлежащего отслеживанию, и сравнивая его с последним состоянием.
В следующей версии приложения я заполняю "объекты" из SQLite DB, используя SQLAlchemy.
Объекты теперь реализованы как декларативные классы SQLAlchemy или списки таких классов.
Чтобы сравнить "старые" и "новые" экземпляры на основе равенства атрибутов, мне пришлось добавить переопределение __eq__
в
мои объекты SQLAlchemy. т.е. экземпляры считаются равными/неизменными, когда атрибуты имеют одинаковые значения.
(Я разместил пример кода внизу этого вопроса).
Технически это работает, но вызывает некоторые архитектурные сигнальные колокола: "Я плыву в неправильном направлении?"
a) Если я добавлю __eq__
и __ne__
переопределяет объекты SQAlchemy, может ли это вызвать SQLAlchemy проблему, когда я позже захочу
для повторного сохранения объектов обратно в базу данных?
b) Насколько далеко до моего приложения должны появляться объекты SQLAlchemy: существует ли "питонская лучшая практика"? То есть нормально или нормально распространять объекты SQLAlchemy с бизнес-логикой/поведением, не связанными с постоянством БД (например, отслеживание изменений); или они должны использоваться только как простые DTO между базой данных и сервером, с бизнес-логикой в других объектах?
Примечание: мне ясно, что данные, представленные клиентам через REST apis и SSE, должны быть абстрагированы от деталей реализации на веб-сервере и БД, так что это не является частью этого вопроса.
sqlalchemy id равенство против равенства ссылок
https://codereview.stackexchange.com/info/93511/data-transfer-objects-vs-entities-in-java-rest-server-application
http://www.mehdi-khalili.com/orm-anti-patterns-part-4-persistence-domain-model/
class EqualityMixin(object):
# extended from the concept in :
# https://stackoverflow.com/info/390250/elegant-ways-to-support-equivalence-equality-in-python-classes
def __eq__(self, other):
classes_match = isinstance(other, self.__class__)
a, b = deepcopy(self.__dict__), deepcopy(other.__dict__)
#compare based on equality our attributes, ignoring SQLAlchemy internal stuff
a.pop('_sa_instance_state', None)
b.pop('_sa_instance_state', None)
attrs_match = (a == b)
return classes_match and attrs_match
def __ne__(self, other):
return not self.__eq__(other)
Ответы
Ответ 1
Я расскажу о том, что происходит за базовым классом, чтобы показать, что переопределения __eq__
и __ne__
прекрасны. Когда вы создаете экземпляр класса Base
, вызывая declarative_base()
, он использует метакласмент за кулисами для его настройки (возможно, стоит прочитать это объяснение это описание метакласса чтобы лучше понять, почему это связано). Он выполняет некоторую настраиваемую настройку, например добавление настраиваемого конструктора в класс Base
и настройку того, как он будет отображаться из объекта в таблицу.
declarative_base()
будет возвращать новый экземпляр класса Base
метакласса DeclarativeMeta
. Здесь задействованы все метаклассы, так что в то время, когда вы создаете класс, который расширяет ваш Base
, он отображает его в таблицу. Если вы пропустите этот путь немного, вы увидите, как он сопоставляет столбцы, которые вы объявляете на своем объекте, в таблицу.
self.cls.__mapper__ = mp_ = mapper_cls(
self.cls, # cls is your model
self.local_table,
**self.mapper_args # the columns you have defined
)
Хотя фактический Mapper, который делает это, выглядит очень сложным и низким уровнем, на данном этапе он работает с первичными ключами и столбцами, а не с фактическими объектами. Это не подтверждает, что оно никогда не использовалось, поэтому я просмотрел способы использования ==
и !=
в источнике и не видел никаких причин для беспокойства.
Что касается вашего второго вопроса, я могу только на самом деле предложить свое собственное мнение - я много раз искал эту тему в прошлом и не нашел большого смысла в использовании "стандартного" SQL-алхимии. До сих пор я использовал SQL Alchemy для нескольких проектов, и кажется, что ваше использование объектов может расширяться настолько, насколько вы можете до сих пор разумно абстрагироваться от жизненного цикла session
. Для меня это похоже на то, что "магия" Алхимии абстрагируется от самих моделей, что, когда сеансы обрабатываются хорошо, они достаточно далеки от уровня данных, что ему не кажется, что бизнес-логика в классах будет путь.