2013-04-10 16 views
17

Czy istnieje sposób tworzenia niestandardowych metod dla obiektu zapytania, aby można było zrobić coś takiego?SQLAlchemy - czy możesz dodać niestandardowe metody do obiektu zapytania?

User.query.all_active() 

Gdzie all_active() jest zasadniczo .filter(User.is_active == True)

i być w stanie odfiltrować z niego?

User.query.all_active().filter(User.age == 30) 
+0

Zastanawiam się, czy coś w tym drugim [Pytanie/odpowiedź] (http://stackoverflow.com/questions/7604967/ sqlalchemy-build-query-filter-dynamically-from-dict) może być w tym celu przydatna? (Jest to jednak mniej niestandardowa metoda ... i bardziej niestandardowy filtr dynamiczny). – summea

Odpowiedz

33

Można podklasy podstawy Query klasę, aby dodać swoje własne metody:

from sqlalchemy.orm import Query 

class MyQuery(Query): 

    def all_active(self): 
    return self.filter(User.is_active == True) 

następnie poinformować SQLAlchemy aby skorzystać z tej nowej klasy zapytania podczas tworzenia sesji (docs here). Z kodu wygląda być może używasz skrzynkowego SQLAlchemy, więc można to zrobić w następujący sposób:

db = SQLAlchemy(session_options={'query_cls': MyQuery}) 

Inaczej byś przekazać argument bezpośrednio do sessionmaker:

sessionmaker(bind=engine, query_cls=MyQuery) 

z mocy prawa teraz ten nowy obiekt zapytania nie jest interesujący, ponieważ w metodzie zakodowano klasę User, więc nie będzie ona działała na nic innego. Lepsza implementacja użyłaby podstawowej klasy zapytania do określenia, który filtr zastosować. Jest to nieco skomplikowane, ale można to zrobić także:

class MyOtherQuery(Query): 

    def _get_models(self): 
    """Returns the query's underlying model classes.""" 
    if hasattr(query, 'attr'): 
     # we are dealing with a subquery 
     return [query.attr.target_mapper] 
    else: 
     return [ 
     d['expr'].class_ 
     for d in query.column_descriptions 
     if isinstance(d['expr'], Mapper) 
     ] 

    def all_active(self): 
    model_class = self._get_models()[0] 
    return self.filter(model_class.is_active == True) 

Wreszcie, ta nowa klasa zapytanie nie będzie używany przez dynamicznych relacji (jeśli występują). Niech ci również użyć go, można przekazać go jako argument podczas tworzenia relacji:

users = relationship(..., query_class=MyOtherQuery) 
+0

To nie działa dla mnie. https://gist.github.com/nickretallack/76bcd65cc0305abcd33b –

+1

@NickRetallack Miałem trochę sukcesów w tworzeniu klasy BaseModel i deklarowaniu atrybutu class query_class. Jednak wydaje się, że jest to problem: https://github.com/mitsuhiko/flask-sqlalchemy/blob/fe67c633f2e6d66a01a1670e5fc6649506358d20/flask_sqlalchemy/__init__.py#L737 – justanr

+0

Mówi się, że jedna sesja może działać tylko z jedną klasą zapytań. Czy możemy zrobić lepiej? i pozwolić tej samej sesji na dynamiczne wybieranie klasy zapytania? Chodzi mi o to, że różne tabele mogą mieć różne klasy zapytań, ale mogę je wykorzystać w tej samej sesji (na przykład podczas korzystania z nich w jednej transakcji). – Mahdi

Powiązane problemy