Правильный способ автоматической фильтрации запросов SQLAlchemy?

Я только что изучил довольно неприятную схему из приложения CRM с sqlalchemy. Во всех таблицах есть удаленный столбец, и я хотел автоматически фильтровать все эти объекты и отношения, помеченные как удаленные. Вот что я придумал:


class CustomizableQuery(Query):
    """An overridden sqlalchemy.orm.query.Query to filter entities

    Filters itself by BinaryExpressions
    found in :attr:`CONDITIONS`
    """

    CONDITIONS = []

    def __init__(self, mapper, session=None):
        super(CustomizableQuery, self).__init__(mapper, session)
        for cond in self.CONDITIONS:
            self._add_criterion(cond)

    def _add_criterion(self, criterion):
        criterion = self._adapt_clause(criterion, False, True)
        if self._criterion is not None:
            self._criterion = self._criterion & criterion
        else:
            self._criterion = criterion

И используется так:

class UndeletedContactQuery(CustomizableQuery):
    CONDITIONS = [contacts.c.deleted != True]

    def by_email(self, email_address):
        return EmailInfo.query.by_module_and_address('Contacts', email_address).contact

    def by_username(self, uname):
        return self.filter_by(twod_username_c=uname).one()

class Contact(object):
    query = session.query_property(UndeletedContactQuery)

Contact.query.by_email('[email protected]')

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

Вот пример картографа:

contacts_map = mapper(Contact, join(contacts, contacts_cstm), {
    '_emails': dynamic_loader(EmailInfo,
                              foreign_keys=[email_join.c.bean_id],
                              primaryjoin=contacts.c.id==email_join.c.bean_id,
                              query_class=EmailInfoQuery),
    })

class EmailInfoQuery(CustomizableQuery):

    CONDITIONS = [email_join.c.deleted != True]
    # More methods here

Это дает мне то, что я хочу: я отфильтровал все удаленные контакты. Я также могу использовать это как аргумент query_class для dynamic_loader в моих картографах. Однако...

  1. Есть ли лучший способ сделать это, я не очень доволен ковырянием во внутренностях такого сложного класса, как Query, как я.
  2. Кто-нибудь решил это другим способом, которым они могут поделиться?

person Ben Ford    schedule 28.05.2009    source источник


Ответы (2)


Вы можете сопоставить с select. Так:

mapper(EmailInfo, select([email_join], email_join.c.deleted == False))
person Ants Aasma    schedule 28.05.2009
comment
Очень красиво, я о таком не знала! - person Ben Ford; 28.05.2009
comment
Я только что пытался сделать это (на другом столе), и это не сработало. Я получил: TypeError: объект «Таблица» не повторяется Любая идея, почему? - person Ben Ford; 29.05.2009
comment
Плохо, первый параметр, который нужно выбрать, — это список столбцов/таблиц, таких как объекты, поэтому email_join должен быть в списке. Я исправлю это. - person Ants Aasma; 29.05.2009

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

person Kylotan    schedule 29.05.2009