【问题标题】:Django - how to filter using QuerySet to get subset of objects?Django - 如何使用 QuerySet 过滤以获取对象的子集?
【发布时间】:2013-08-29 04:46:35
【问题描述】:

根据documentation

filter(**kwargs) 返回一个包含匹配对象的新 QuerySet 给定的查找参数。

查找参数 (**kwargs) 应采用中所述的格式 下面的字段查找。多个参数通过 AND 连接 底层 SQL 语句。

对我来说,这表明它将返回原始集合中的项目子集。 但是我似乎遗漏了一些东西,因为下面的例子并不像我预期的那样:

>>> kids = Kid.objects.all()
>>> tuple(k.name for k in kids)
(u'Bob',)
>>> toys = Toy.objects.all()
>>> tuple( (t.name, t.owner.name) for t in toys)
((u'car', u'Bob'), (u'bear', u'Bob'))
>>> subsel = Kid.objects.filter( owns__in = toys )
>>> tuple( k.name for k in subsel )
(u'Bob', u'Bob')
>>> str(subsel.query)
'SELECT "bug_kid"."id", "bug_kid"."name" FROM "bug_kid" INNER JOIN "bug_toy" ON ("bug_kid"."id" = "bug_toy"."owner_id") WHERE "bug_toy"."id" IN (SELECT U0."id" FROM "bug_toy" U0)'

正如您在上面看到的 subsel 最终返回重复记录,这不是我想要的。我的问题是获取子集的正确方法是什么? (注意:按定义设置不会多次出现同一个对象)

解释 为什么 它的行为也很好,因为对我来说 filter 意味着你通过 filter() 实现的目标Python 中的 -in 函数。即:获取满足要求的元素(或者换句话说,丢弃不满足要求的元素)。而且这个定义似乎不允许引入/复制对象。

我知道可以将 distinct() 应用于整个事情,但这仍然会导致相当丑陋(并且可能比可能慢)的查询:

>>> str( subsel.distinct().query )
'SELECT DISTINCT "bug_kid"."id", "bug_kid"."name" FROM "bug_kid" INNER JOIN "bug_toy" ON ("bug_kid"."id" = "bug_toy"."owner_id") WHERE "bug_toy"."id" IN (SELECT U0."id" FROM "bug_toy" U0)'

为了完整性,我的 models.py

from django.db import models

class Kid(models.Model):
    name = models.CharField(max_length=200)

class Toy(models.Model):
    name = models.CharField(max_length=200)
    owner = models.ForeignKey(Kid, related_name='owns')

编辑

与@limelight 聊天后得出的结论是,我的 问题是我希望filter() 的行为符合字典定义。以及它在 Python 或任何其他健全的框架/语言中的工作方式。

更准确地说,如果我设置了A = {x,y,z} 并调用A.filter( <predicate> ),我不希望任何元素被重复。然而,使用 Django 的 QuerySet,它的行为如下:

A = {x,y,z}
A.filter( <predicate> )
# now A i.e. = {x,x}

所以首先问题是方法名称不合适(类似 ma​​tch() 会好得多)。 第二件事是,我认为创建比 Django 允许的更有效的查询是可能的。我可能错了,如果我有一点时间,我可能会尝试检查是否属实。

【问题讨论】:

  • 所以你想要独特的儿童领域?
  • 给定玩具子集,我想获得拥有这些玩具的孩子子集。实际上我的情况稍微复杂一些,因为实际上我有“所有者”和“last_played_with”字段。我想要的是拥有或玩过这些玩具的所有孩子的清单。如果可能的话,我想了解为什么 filter() 会重复对象。
  • 你能提供完整的模型吗?机智 last_played 和其他相关领域。
  • 使用distinct() 不是一个坏习惯,也不会减慢速度。这实际上是不编写自己的 SQL 的推荐方法。至于filter(),它实际上在文档中By default, a QuerySet will not eliminate duplicate rows
  • 根据我对 filter() 应该如何工作的理解,它不应该引入任何连接(至少在这种情况下),而只是扩展 WHERE 子句的内容。尽管我理解为什么该查询返回它返回的内容,但我不明白为什么我会得到这样的查询。更不用说 Django 应该将我与实际的 SQL 隔离开来,所以我想了解该短语的语义。并且没有“__in”并不意味着它会产生重复的项目。

标签: python django


【解决方案1】:

这有点难看,但有效(没有任何类型安全):

toy_owners = Toy.objects.values("owner_id")  # optionally with .distinct()
Kid.objects.filter(id__in=toy_owners)

如果性能不是问题,我认为@limelights 是对的。

PS!我在 Django 1.6b2 上测试了您的查询,得到了同样不必要的复杂查询。

【讨论】:

    【解决方案2】:

    您可以使用 GROUP BY(在 django 中注释)代替 DISTINCT 来获得不同的孩子。

    toy_owners = Toy.objects.values_list("owner_id", flat=True).distinct()
    Kid.objects.only('name').filter(pk__in=toy_owners).annotate(count=Count('owns'))
    

    【讨论】:

    • 这不起作用,您正在使用两个查询并且您没有使用 GROUP BY。试试你的代码: Kid(id=100, name="Peter").save() Kid(id=101, name="John").save() Toy(name="car", owner_id=100)。 save() Toy(name="bear", owner_id=100).save()
    • 在你编辑之后,你有两个查询,最后一个是SELECT "app_kid"."id", "app_kid"."name", COUNT("app_toy"."id") AS "count" FROM "app_kid" LEFT OUTER JOIN "app_toy" ON ( "app_kid"."id" = "app_toy"."owner_id" ) WHERE "app_kid"."id" IN (100) GROUP BY "app_kid"."id", "app_kid"."name"
    猜你喜欢
    • 2013-09-29
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-20
    • 2015-07-16
    相关资源
    最近更新 更多