Django从注释计数中排除

时间:2015-08-06 09:58:28

标签: python django django-models django-orm

我有以下申请:

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()

我想用已完成作业的数量来注释Workers查询。

我会尝试使用以下脚本:

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'))
# >>> []

上次查询的结果为空。如何从反向关系中排除元素?

Django 1.8,python 2.7

UPD。我希望查询集中的所有工作人员,即使是那些没有工作的人

4 个答案:

答案 0 :(得分:9)

更新:好的我用这个来玩生成解决方案,我想我是用Conditional Expressions得到的:

  

条件表达式允许你使用if ... elif ... else逻辑   过滤器,注释,聚合和更新。有条件的   expression计算表的每一行的一系列条件   并返回匹配的结果表达式。

注意: Conditional Expressions (例如CaseWhen)在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,它将是一个Integer,显示工作人员已完成的工作数量:)

答案 1 :(得分:2)

以下

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 proposed feature(已关闭)

this SO QA

UPDATE: Django 1.8引入了条件表达式。 @ BogdiG的答案使用这些新的运算符来解决问题。奖励!

答案 2 :(得分:1)

更新

如果你想为每个工人计算完成的工作,我们可以通过.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现在支持@BogdiG's answer中描述的语法中条件表达式SUM(CASE WHEN is_completed = True THEN 1 ELSE 0 END)的Python映射,这很棒。

删除了有关filter/exclude

的内容

答案 3 :(得分:1)

这里有 2 个选项。第一个是 Q 上的直接 Count 过滤器:

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 过滤器(有时需要它,因为 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))
)