Условная аннотация Django

Я удивлен, что этого вопроса, по-видимому, еще не существует. Если это так, пожалуйста, помогите мне найти его.

Я хочу использовать annotate (Count) и order_by, но я не хочу подсчитывать каждый экземпляр связанного объекта, а только те, которые соответствуют определенному критерию.

А именно, чтобы я мог перечислить ласточек по количеству унесенных ими зеленых кокосов:

swallow.objects.annotate(num_coconuts=Count('coconuts_carried__husk__color = "green"').order_by('num_coconuts')

person jMyles    schedule 09.08.2011    source источник


Ответы (2)


Это должно быть правильным путем.

swallow.objects.filter(
    coconuts_carried__husk__color="green"
).annotate(
    num_coconuts=Count('coconuts_carried')
).order_by('num_coconuts')

Обратите внимание, что когда вы фильтруете связанное поле, в необработанном SQL это переводится как LEFT JOIN плюс WHERE. В конце концов аннотация воздействует на набор результатов, который содержит только связанные строки, выбранные из первого фильтра.

person rewritten    schedule 09.08.2011
comment
Действительно, кажется, что это работает, но по причинам, которые вы указываете, это совершенно нелогично. Я ожидаю, что этот набор запросов даст мне всех ласточек, которые когда-либо несли хотя бы один зеленый кокос, упорядоченных по общему количеству несущих кокосов, независимо от цвета. - person jMyles; 09.08.2011
comment
@jMyles: в документации указано, что это зависит от порядок предложений аннотации и фильтрации - наоборот, он будет вести себя так, как вы описываете. - person Daniel Roseman; 09.08.2011
comment
Что, если вы хотите включить ласточек, у которых нет зеленых кокосов? - person Gordon Wrigley; 28.10.2013
comment
@tolomea прошло много времени, и Django, возможно, за это время изменился, но LEFT JOIN сохраняет записи, где правая часть соединения пуста, и устанавливает столбцы как NULL. Это должно привести к включению ласточек, не несущих зеленый кокос. - person rewritten; 28.10.2013

Для Django >= 1.8:

from django.db.models import Sum, Case, When, IntegerField

swallow.objects.annotate(
    num_coconuts=Sum(Case(
        When(coconuts_carried__husk__color="green", then=1),
        output_field=IntegerField(),
    ))
).order_by('num_coconuts')
person Erik Telepovský    schedule 09.11.2016