Django исключить из числа аннотаций

У меня есть следующее приложение:

from django.db import models


class Worker(models.Model):
    name = models.CharField(max_length=60)

    def __str__(self):
        return self.name


class Job(models.Model):
    worker = models.ForeignKey(Worker)
    is_completed = models.BooleanField()

Я хочу аннотировать запрос работников с количеством выполненных работ.

Я постараюсь сделать это с помощью следующего скрипта:

from myapp.models import Worker, Job
from django.db.models import Count

w = Worker.objects.create(name='Worker1')
Job.objects.create(worker=w, is_completed=False)
Job.objects.create(worker=w, is_completed=False)
Job.objects.create(worker=w, is_completed=True)
Job.objects.create(worker=w, is_completed=True)

workers = Worker.objects.all().annotate(num_jobs=Count('job'))
workers[0].num_jobs    
# >>> 4
workers = Worker.objects.all().exclude(job__is_completed=False).annotate(num_jobs=Count('job'))
# >>> []

Результат последнего запроса пуст. Как исключить элементы из обратной связи?

Джанго 1.8, питон 2.7

UPD. Я хотел бы, чтобы все работники в queryset, даже те, кто имеет ноль рабочих мест

4 ответа

Решение

обновление: хорошо, я немного поиграл с этим, чтобы сгенерировать решение, и я думаю, что получил его, используя условные выражения:

Условные выражения позволяют использовать логику if... elif... else в фильтрах, аннотациях, агрегатах и ​​обновлениях. Условное выражение оценивает ряд условий для каждой строки таблицы и возвращает соответствующее выражение результата.

Примечание. Условные выражения (например, Case а также When) являются новыми в Django 1.8, на что указывает @Pynchia

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

workers = Worker.objects.annotate(
              num_jobs=Sum(
                  Case(
                      When(job__is_completed=True, then=1),
                      default=0,  
                      output_field=IntegerField()
                  )
               )
           )

теперь у каждого работника будет num_jobs, который будет целым числом, показывающим, сколько выполненных заданий у этого работника есть:)

Здесь есть 2 варианта. Первый - это прямой фильтр:

      from myapp.models import Worker
from django.db.models import Count, Q

workers = Worker.objects.annotate(
    completed_jobs_count=Count("job", filter=Q(job__is_completed=True))
)

А второй исключает Q фильтр включен (иногда это необходимо, потому что Count не имеет прямых exclude вариант):

      from myapp.models import Worker
from django.db.models import Count, Q

workers = Worker.objects.annotate(
    completed_jobs_count=Count("job", filter=~Q(job__is_completed=False))
)

Следующие

workers = Worker.objects.filter(job__is_completed=True)).annotate(num_jobs=Count('job__is_completed'))

комментирует тех работников, которые выполнили хотя бы одну работу. Те, чей счет завершен до нуля, не включены в набор результатов запроса.

Если вы хотите, чтобы ВСЕ рабочие появлялись в результирующем наборе запросов, было бы здорово, если бы мы могли написать

workers = Worker.objects.annotate(num_jobs=CountIf('job__is_completed', job__is_completed=True))

но, к сожалению, мы не можем. Так что я здесь не в своей тарелке и считаю, что мой ответ неполный. Я приветствую вмешательство кого-то более компетентного, чем я, который мог бы пролить свет на этот вопрос.

Для справки смотрите эту предложенную Django функцию (закрыто)

и это так QA

ОБНОВЛЕНИЕ: Django 1.8 ввел условные выражения. Ответ @BogdiG использует такие новые операторы для решения проблемы. Престижность!

Обновить

Если вы хотите подсчитать выполненную работу для каждого работника, мы могли бы использовать подзапрос через .extra():

Worker.objects.extra(select={'jobs_done': 
     'SELECT COUNT(*) FROM {job_tbl} WHERE worker_id = {worker_tbl}.id AND is_completed'
     .format(worker_tbl=Worker._meta.db_table, job_tbl=Job._meta.db_table)})

Хорошо, что Django теперь поддерживает отображение условных выражений в Python SUM(CASE WHEN is_completed = True THEN 1 ELSE 0 END) в синтаксисе, описанном в ответе @BogdiG.


Удаленные вещи о filter/exclude

Другие вопросы по тегам