Python Django - 如何使用 QuerySet 过滤以获取对象的子集?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18448468/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Django - how to filter using QuerySet to get subset of objects?
提问by elmo
According to documentation:
根据文档:
filter(**kwargs) Returns a new QuerySet containing objects that match the given lookup parameters.
The lookup parameters (**kwargs) should be in the format described in Field lookups below. Multiple parameters are joined via AND in the underlying SQL statement.
filter(**kwargs) 返回一个新的 QuerySet,其中包含与给定查找参数匹配的对象。
查找参数 (**kwargs) 应采用下面字段查找中描述的格式。多个参数通过底层 SQL 语句中的 AND 连接。
Which to me suggests it will return a subset of items that were in original set. However I seem to be missing something as below example does not behave as I would expect:
对我来说,这表明它将返回原始集合中项目的子集。但是我似乎遗漏了一些东西,因为下面的例子不像我期望的那样表现:
>>> 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)'
As you can see in above subselends up returning duplicate records, which is not what I wanted. My question is what is the properway to get subset? (note: set by definition will not have multiple occurrences of the same object)
正如您在上面的subsel 中看到的那样,最终返回重复的记录,这不是我想要的。我的问题是获取子集的正确方法是什么?(注意:根据定义设置不会多次出现同一对象)
Explanation as to whyit behaves like that would be also nice, as to me filtermeans what you achieve with filter()built-in function in Python. Which is: take elements that fulfill requirement (or in other words discard ones that do not). And this definition doesn't seem to allow introduction/duplication of objects.
解释为什么它的行为也会很好,因为对我来说filter意味着你用Python 中的filter()内置函数实现的。即:采用满足要求的元素(或换句话说丢弃不满足的元素)。而且这个定义似乎不允许引入/复制对象。
I know can aplly distinct()to the whole thing, but that still results in rather ugly (and probably slower than could be) query:
我知道可以对整个事情应用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)'
My models.pyfor completeness:
为了完整性,我的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')
edit:
编辑:
After a chat with @limelight the conclusion is that myproblem is that I expect filter()to behave according to dictionary definition. And i.e. how it works in Python or any other sane framework/language.
在与@limelight 聊天后,结论是我的问题是我希望filter()能够根据字典定义进行操作。即它如何在 Python 或任何其他健全的框架/语言中工作。
More precisely if I have set A = {x,y,z}
and I invoke A.filter( <predicate> )
I don't expect any elements to get duplicated. With Django's QuerySet however it behaves like this:
更准确地说,如果我已经设置A = {x,y,z}
并调用,A.filter( <predicate> )
我不希望任何元素被复制。然而,使用 Django 的 QuerySet,它的行为是这样的:
A = {x,y,z}
A.filter( <predicate> )
# now A i.e. = {x,x}
So first of all the issue is inappropriate method name (something like match()would be much better). Second thing is that I think it is possible to create more efficient query than what Django allows me to. I might be wrong on that, if I will have a bit of time I will probably try to check if that is true.
所以首先问题是不合适的方法名称(像match()这样的东西会好得多)。第二件事是,我认为可以创建比 Django 允许的更有效的查询。我可能是错的,如果我有一点时间,我可能会尝试检查这是否属实。
回答by chlunde
This is kind of ugly, but works (without any type safety):
这有点难看,但有效(没有任何类型安全):
toy_owners = Toy.objects.values("owner_id") # optionally with .distinct()
Kid.objects.filter(id__in=toy_owners)
If performance is not an issue, I think @limelights is right.
如果性能不是问题,我认为@limelights 是对的。
PS! I tested your query on Django 1.6b2 and got the same unnecessary complex query.
附注!我在 Django 1.6b2 上测试了您的查询,并得到了相同的不必要的复杂查询。
回答by P??x?L?
Instead DISTINCT you can use GROUP BY (annotate in django) to get distinct kids.
相反,您可以使用 GROUP BY(在 django 中注释)来获得不同的孩子。
toy_owners = Toy.objects.values_list("owner_id", flat=True).distinct()
Kid.objects.only('name').filter(pk__in=toy_owners).annotate(count=Count('owns'))