2010-01-23 19 views
10

Soy nuevo en sqlalchemy, y si bien la documentación parece bastante exhaustiva, no pude encontrar una manera de hacer todo lo que quiero.Cómo devolver el conteo de entidades relacionadas en sqlalchemy query

Digamos que tengo dos tablas: foro y publicación. Cada foro tiene un foro principal y cualquier cantidad de publicaciones. Lo que quiero es:

  • Una lista de los foros de alto nivel
  • impaciencia cargados-foros accesibles a través de los foros de alto nivel
  • Es un recuento de mensajes para cada niño foro

Así que comencé con:

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

Lo que me da todo el nivel superior foros. Por supuesto, acceder a los foros secundarios produce n consultas selectivas.

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

Esto resuelve el n problema de selección.

Ahora mi mejor conjetura es algo como esto:

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

Pero todo lo que consigo es:

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

He probado algunas variaciones, pero no tengo más. Sólo por la claridad que estoy buscando el equivalente de este 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 

Respuesta

8

porque desea que el número de puestos para ser accesible en el Foro de los objetos secundarios que tendrá que declarar como una propiedad de la columna cuando la creación de la mapeadores. La declaración de propiedad de la columna debe ser algo como esto (suponiendo que el uso declarativo):

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) 

A continuación, se puede plantear la consulta como la siguiente:

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

Otra opción sería la de seleccionar a los niños y los recuentos por separado . En este caso, deberá hacer la agrupación de resultados usted mismo:

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 
Cuestiones relacionadas