Разработать шаблон для организации нетривиальных запросов ORM? - PullRequest
2 голосов
/ 15 февраля 2010

Я разрабатываю веб-API с 10 или около того таблицами в бэкэнде, с несколькими ассоциациями «один ко многим» и «многие ко многим». По сути, API - это оболочка базы данных, которая выполняет проверенные обновления и условные запросы. Он написан на Python, и я использую SQLAlchemy для ORM и CherryPy для обработки HTTP.

Пока что я разделил 30 запросов, которые API выполняет, на свои собственные функции, которые выглядят так:

# in module "services.inventory"
def find_inventories(session, user_id, *inventory_ids, **kwargs):
    query = session.query(Inventory, Product)
    query = query.filter_by(user_id=user_id, deleted=False)
    ...
    return query.all()

def find_inventories_by(session, app_id, user_id, by_app_id, by_type, limit, page):
    ....

# in another service module
def remove_old_goodie(session, app_id, user_id):
    try:
        old = _current_goodie(session, app_id, user_id)
        services.inventory._remove(session, app_id, user_id, [old.id])
    except ServiceException, e:
        # log it and do stuff
....

Обработчик запросов CherryPy вызывает методы запросов, которые при необходимости разбросаны по нескольким сервисным модулям. Обоснование этого решения заключается в том, что, поскольку им необходим доступ к нескольким классам моделей, они не принадлежат отдельным моделям, а также эти запросы к базе данных должны быть отделены от прямой обработки доступа к API.

Я понимаю, что приведенный выше код может называться Иностранные методы в сфере рефакторинга. Я вполне мог бы жить с этим способом организации на некоторое время, но, поскольку все начинает выглядеть немного грязно, я ищу способ реорганизовать этот код.

  • Поскольку запросы напрямую связаны с API и его бизнес-логикой, их трудно обобщать, как геттеры и сеттеры.
  • Пахнет повторять аргумент session таким образом, но, поскольку текущая реализация API создает новый экземпляр обработчика CherryPy для каждого вызова API и, следовательно, объект session, не существует глобального способа добраться до ток session.

Существует ли устоявшаяся схема организации таких запросов? Должен ли я придерживаться внешних методов и просто попытаться унифицировать сигнатуру функции (порядок аргументов, соглашения об именах и т. Д.)? Что бы вы предложили?

Ответы [ 2 ]

1 голос
/ 15 февраля 2010

Стандартный способ иметь глобальный доступ к текущему сеансу в многопоточной среде: ScopedSession . Есть несколько важных аспектов, которые необходимо решить при интеграции с вашей средой, в основном управление транзакциями и очистка сеансов между запросами. Распространенным шаблоном является наличие в модуле autocommit = False (по умолчанию) ScopedSession и обертывание любого выполнения бизнес-логики в предложении try-catch, которое откатывается в случае исключения и фиксируется, если метод завершился успешно, затем, наконец, вызывает Session.remove (). Затем бизнес-логика импортирует объект Session в глобальную область и использует его как обычный сеанс.

Кажется, существует существующий модуль интеграции CherryPy-SQLAlchemy , но, поскольку я не слишком знаком с CherryPy, я не могу комментировать его качество.

Имея запросы, инкапсулированные в функции, просто отлично. Не все должно быть в классе. Если их становится слишком много, просто разбейте их на отдельные модули по темам.

То, что я нашел полезным, - это слишком раздробить фрагменты общих критериев. Они, как правило, подходят как классовые методы на модельных классах. Помимо повышения читабельности и уменьшения дублирования, они работают как скрытые от реализации абстракции до некоторой степени, делая рефакторинг базы данных менее болезненным. (Пример: вместо (Foo.valid_from <= func.current_timestamp()) & (Foo.valid_until > func.current_timestamp()) вы бы получили Foo.is_valid())

1 голос
/ 15 февраля 2010

SQLAlchemy настоятельно рекомендует, чтобы создатель сеанса был частью некоторой глобальной конфигурации.

Предполагается, что сеансмейкер () функция вызывается в глобальном сфера применения, а также возвращаемый класс будет доступен остальная часть приложения в качестве один класс используется для создания экземпляра сессий.

Запросы, которые находятся в отдельных модулях, не являются интересной проблемой. Django ORM работает таким образом. Веб-сайт обычно состоит из нескольких «приложений» Django, что походит на ваш сайт с множеством «сервисных модулей».

Соединение нескольких сервисов является целью приложения. Есть не так много альтернатив, которые лучше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...