【问题标题】:Django filter() on field of related model相关模型字段上的 Django filter()
【发布时间】:2013-06-12 12:25:03
【问题描述】:

我认为我缺少一些关于 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))

我的期望是这将打印“2”,这是至少有一个带有 flag=True 的项目的集合的数量。这种预期是基于https://docs.djangoproject.com/en/1.5/topics/db/queries/#lookups-that-span-relationships 的文档,其中有一个示例说“此示例检索所有条目对象,其博客名称为 'Beatles Blog'”。

但是,上面的调用实际上打印了“6”,这是具有 flag=True 的 Item 记录的数量。但是,返回的实际对象是 Collection 对象。似乎它多次返回同一个 Collection 对象,对于每个带有 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"))]

第一种情况打印“[2,4]”,但第二种情况打印“[8,16]”!!!

填充函数:

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

【问题讨论】:

    标签: django django-models django-queryset


    【解决方案1】:

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

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

    以下输出如预期的“2”:

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

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

    【讨论】:

      猜你喜欢
      • 2018-12-30
      • 2020-01-19
      • 1970-01-01
      • 2014-12-03
      • 1970-01-01
      • 1970-01-01
      • 2020-11-02
      • 2016-06-30
      • 2014-09-04
      相关资源
      最近更新 更多