Django filter()在相关模型的字段上

时间:2013-06-16 10:43:05

标签: django django-models django-queryset

我认为我遗漏了一些关于Django的filter()方法应该如何运作的基本和基本的东西。

使用以下型号:

class Collection(models.Model): 
    pass

class Item(models.Model):
    flag = models.BooleanField()
    collection =  models.ForeignKey(Collection)

并通过调用问题底部的populate()函数提供的数据,尝试在./manage.py shell中执行以下命令:

len(Collection.objects.filter(item__flag=True))

我的期望是这会打印&#34; 2&#34;,这是至少有一个标志为True的项目的集合数量。这个期望是基于https://docs.djangoproject.com/en/1.5/topics/db/queries/#lookups-that-span-relationships的文档,其中有一个例子说&#34;这个例子检索所有Entry对象,其博客的名称是&#39; Beatles Blog&#39;&#34;。< / p>

但是,上面的调用实际上会打印&#34; 6&#34;,这是具有flag = True的Item记录的数量。但是,返回的实际对象是Collection对象。它似乎多次返回相同的Collection对象,对于每个对应的Item记录,其中flag = True。这可以通过以下方式确认:

queryset = Collection.objects.filter(item__flag=True)
queryset[0] == queryset[1]

打印True。

这是正确的行为吗?如果是的话,理由是什么?如果它是预期的,文档可以被解释为严格正确,但它没有说每个对象可以多次返回。

这是一个相关的例子,这似乎是非常令人惊讶的(或者只是错误的)行为。在一个自定义模型管理器添加了exclude()调用然后调用者添加了filter()的情况下,它引起了我的注意:

from django.db.models import Count    
[coll.count for coll in Collection.objects.filter(item__flag=True).annotate(count=Count("item"))]
[coll.count for coll in Collection.objects.exclude(item=None).filter(item__flag=True).annotate(count=Count("item"))]

第一个案例打印&#34; [2,4]&#34;,但第二个打印&#34; [8,16]&#34; !!!

填充功能:

def populate():
    Collection.objects.all().delete()

    collection = Collection()
    collection.save()
    item = Item(collection=collection, flag=True)
    item.save()
    item = Item(collection=collection, flag=True)
    item.save()
    item = Item(collection=collection, flag=False)
    item.save()
    item = Item(collection=collection, flag=False)
    item.save()

    collection = Collection()
    collection.save()
    item = Item(collection=collection, flag=True)
    item.save()
    item = Item(collection=collection, flag=True)
    item.save()
    item = Item(collection=collection, flag=True)
    item.save()
    item = Item(collection=collection, flag=True)
    item.save()

    collection = Collection()
    collection.save()
    item = Item(collection=collection, flag=False)
    item.save()
    item = Item(collection=collection, flag=False)
    item.save()
    item = Item(collection=collection, flag=False)
    item.save()
    item = Item(collection=collection, flag=False)
    item.save()

1 个答案:

答案 0 :(得分:14)

事实证明,这有两个部分。首先是distinct()方法,doc说:

  

默认情况下,QuerySet不会消除重复的行。在实践中,   这很少是一个问题,因为简单的查询如   Blog.objects.all()不会引入重复结果的可能性   行。但是,如果您的查询跨越多个表,则可以   在评估QuerySet时获取重复的结果。那是你的时候   使用distinct()。

以下输出“2”如预期:

len(Collection.objects.filter(item__flag=True).distinct())

但是,这对我使用annotate()提供的更复杂的示例没有帮助。事实证明这是一个已知问题的实例:https://code.djangoproject.com/ticket/10060