我认为我遗漏了一些关于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()
答案 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。