在Django中过滤注释

时间:2020-08-05 12:43:19

标签: python-3.x django django-models

我正在用Count注释QuerySet,但是我只想对某些对象进行计数,因此我正在使用过滤器:

    objects = Parent.objects.annotate(num_children=Count('child',filter=Q(deleted_at=None)))

过滤器不起作用,每个对象的计数似乎都忽略了过滤器,即,它也计算了具有过滤器的子容器

deleted_at != None

模型

class child(models.Model):
    parent = models.ForeignKey('project.Parent', on_delete=models.CASCADE)
    deleted_at = models.DateTimeField(blank=True, null=True)

有人有解决方案吗? 预先感谢。

修改

使用Q(deleted_at__isnull=True)也不起作用。与~Q(deleted_at=None)奇怪的是,我得到了预期的行为...

1 个答案:

答案 0 :(得分:3)

TL; DR

使用 Q(...) 对象进行过滤时,必须提供关于 Parent 模型的查找。

因此,它应该是 filter=Q(child__deleted_at=None) 而不是filter=Q(deleted_at=None)

objects = Parent.objects.annotate(
    num_children=Count('child', filter=Q(child__deleted_at=None))
)

长答案

为重现错误/问题行为,我创建了以下两个简单模型,

class Parent(models.Model):
    name = models.CharField(max_length=50)

    class Meta:
        db_table = 'parent_table'

    def __str__(self):
        return self.name


class Child(models.Model):
    parent = models.ForeignKey(Parent, on_delete=models.CASCADE, related_name='children')
    deleted_at = models.DateTimeField(blank=True, null=True)

    class Meta:
        db_table = 'child_table'

    def __str__(self):
        return f'{self.parent} -- {self.deleted_at}'

注释:

  1. 我设置了related_name='children',它在查询反向关系时更“有意义”。如果您不熟悉related_name,请阅读更多

    a。 What is related_name in Django -- (SO Post)

    b。 Following backwards relationship -- (Django doc)

    c。 ForeignKey.related_name -- (Django doc)

  2. 我已经设置了db_table,因为某些原因,我将使用RawSQL查询数据库。您不必进行设置。

现在,我在数据库中填充了一些虚拟数据,结果如下:

Joined table view

然后我打开Django shell并运行以下查询,

In [2]: from django.db.models import Q,Count                                                                                                                                                                       

In [3]: parent_objects = Parent.objects.annotate( 
   ...:     num_children=Count('children', filter=Q(children__deleted_at=None)) 
   ...: )                                                                                                                                                                                                          

In [4]: for parent in parent_objects: 
   ...:     print(parent.id, '--', parent.name, ' :: ', parent.num_children) 
   ...:                                                                                                                                                                                                            
1 -- parent-1  ::  2
2 -- parent-2  ::  0
3 -- parent-3  ::  1

或者, 原始SQL视图,

annotate result