Как вернуть количество связанных объектов в запросе sqlalchemy - PullRequest
10 голосов
/ 24 января 2010

Я новичок в sqlalchemy, и хотя документация кажется довольно полной, я не смог найти способ сделать то, что я хочу.

Скажем, у меня есть две таблицы: форум и пост. У каждого форума есть родительский форум и любое количество постов. То, что я хочу, это:

  • Список форумов верхнего уровня
  • Загруженные дочерние форумы, доступные через форумы верхнего уровня
  • Количество сообщений для каждого ребенка форума

Итак, я начал с:

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

Что дает мне все форумы верхнего уровня. Конечно, доступ к дочерним форумам дает n запросов select.

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

Это решает проблему выбора n.

Теперь моя лучшая догадка выглядит примерно так:

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

Но все, что я получаю, это:

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

Я пробовал несколько вариантов, но больше ничего не получил. Просто для ясности я ищу эквивалент этого 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

1 Ответ

8 голосов
/ 25 января 2010

Поскольку вы хотите, чтобы количество сообщений было доступно в дочерних объектах форума, вам необходимо объявить его как свойство столбца при настройке картографов. Объявление свойства столбца должно выглядеть примерно так (при условии использования декларативного):

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)

Тогда вы можете сформулировать свой запрос следующим образом:

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

Другим вариантом будет выбор детей и подсчет отдельно. В этом случае вам нужно сгруппировать результат самостоятельно:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...