2010-01-23 6 views
10

zurückgegeben werden Ich bin neu zu sqlalchemy, und während die Dokumentation ziemlich gründlich scheint, konnte ich keinen Weg finden, ganz was ich will.Wie die Anzahl der verbundenen Entitäten in sqlalchemy Abfrage

Sagen wir, ich habe zwei Tabellen: Forum und Post. Jedes Forum hat ein Elternforum und eine beliebige Anzahl von Beiträgen. Was ich will, ist:

  • Eine Liste von Top-Level-Foren
  • Eifrig geladen Kind Foren zugänglich durch die Top-Level-Foren
  • Die Anzahl der Beiträge für jedes Kind Forum

Also begann ich mit:

query(Forum).filter(Forum.parent==None).all() 

, die alle mir die oberste Ebene gibt Foren. Der Zugriff auf die untergeordneten Foren führt natürlich zu ausgewählten Abfragen.

query(Forum).options(eagerload('children')).filter(Forum.parent==None).all() 

Dies löst das Problem n select.

Jetzt ist meine beste Vermutung geht in etwa so:

query(Forum, func.count(Forum.children.posts)).options(eagerload('children')).filter(Forum.parent==None).group_by(Forum.children.id).all() 

Aber alles, was ich bekommen ist:

AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object has an attribute 'posts' 

Ich habe versucht, ein paar Variationen, aber noch nicht weiter kam. Nur für Klarheit Ich suche nach dem Äquivalent dieser SQL:

select Forum.*, Child.*, count(Post.id) 
from Forum 
left join Forum Child on Child.parent = Forum.id 
left join Message on Message.forum = Child.id 
where Forum.parent is null 
group by Child.id 

Antwort

8

Weil Sie wollen, dass die Post-Zähler zugänglich auf das Kind Forum sein, Objekte müssen Sie es als eine Spalte Eigenschaft erklären, wenn die Einrichtung Mapper. Die Spalte Eigenschaft Erklärung wie folgt aussehen sollte (vorausgesetzt, Sie deklarative verwenden):

Forum.post_count = column_property(select([func.count()], 
     Message.__table__.c.forum == Forum.__table__.c.id 
    ).correlate(Forum.__table__).as_scalar().label('post_count'), 
    deferred=True) 

Dann können Sie Phrase Ihre Abfrage wie folgt:

query(Forum).filter_by(parent=None).options(
    eagerload('children'), 
    undefer('children.post_count')) 

Eine andere Möglichkeit wäre es, die Kinder und zählt wählen separat . In diesem Fall müssen Sie die Ergebnisgruppierung selbst vornehmen:

ChildForum = aliased(Forum) 
q = (query(Forum, ChildForum, func.count(Message.id)) 
     .filter(Forum.parent == None) 
     .outerjoin((ChildForum, Forum.children)) 
     .outerjoin(ChildForum.posts) 
     .group_by(Forum, ChildForum) 
    ) 

from itertools import groupby 
from operator import attrgetter 

for forum, childforums in groupby(q, key=attrgetter('Node')): 
    for _, child, post_count in childforums: 
     if child is None: 
      # No children 
      break 
     # do something with child 
Verwandte Themen