2010-01-23 15 views
10

Sono nuovo in sqlalchemy e, mentre la documentazione sembra abbastanza approfondita, non sono riuscito a trovare un modo per fare esattamente quello che voglio.Come restituire il conteggio delle entità correlate nella query sqlalchemy

Dire che ho due tavoli: forum e posta. Ogni forum ha un forum principale e un numero qualsiasi di post. Quello che voglio è:

  • Una lista di forum di alto livello
  • Ardentemente caricati-forum accessibili attraverso il forum di alto livello
  • Un conteggio dei posti per bambino forum

Quindi ho iniziato con:

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

Quale mi dà tutto il livello più alto forum. Naturalmente l'accesso ai forum figli produce n query selezionate.

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

Questo risolve il problema di selezione n.

Ora la mia migliore ipotesi più o meno così:

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

Ma tutto quello che ottiene è:

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

Ho provato un paio di varianti, ma non ho più alcuna. Solo per chiarezza che sto cercando l'equivalente di questo 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 

risposta

8

Perché si desidera che il numero dei tuoi messaggi sia accessibile sul Forum bambino oggetti è necessario dichiararla come una proprietà di colonna durante l'impostazione del mapper. La dichiarazione di proprietà della colonna dovrebbe essere simile a questo (a patto di utilizzare dichiarativa):

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) 

Poi si può frase la query come questa:

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

Un'altra opzione sarebbe quella di selezionare i bambini e conta separatamente . In questo caso è necessario effettuare il raggruppamento dei risultati:

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 
Problemi correlati