django中具有多个参数的过滤器和链式过滤器之间的区别
Posted
技术标签:
【中文标题】django中具有多个参数的过滤器和链式过滤器之间的区别【英文标题】:difference between filter with multiple arguments and chain filter in django 【发布时间】:2011-07-29 09:51:05 【问题描述】:django中的多参数过滤器和链式过滤器有什么区别?
【问题讨论】:
过滤器可以有多个参数吗? ***.com/questions/420703/… 【参考方案1】:大多数时候,一个查询只有一组可能的结果。
在处理 m2m 时会用到链接过滤器:
考虑一下:
# will return all Model with m2m field 1
Model.objects.filter(m2m_field=1)
# will return Model with both 1 AND 2
Model.objects.filter(m2m_field=1).filter(m2m_field=2)
# this will NOT work
Model.objects.filter(Q(m2m_field=1) & Q(m2m_field=2))
欢迎提供其他示例。
【讨论】:
另一个例子:它不仅限于 m2m,这也可能发生在一对多 - 使用反向查找,例如在 ForeignKey 上使用related_name 感谢您的解释!在此之前,我认为最后一个示例和第二个示例是相等的,所以最后一个示例对我不起作用(错误的查询结果),我花了很多时间进行搜索。第二个例子对我很有帮助。正如 Wim 所说,这可用于反向一对多关系,就像我的情况一样。【参考方案2】:您可以使用连接模块查看原始 sql 查询以进行比较。正如 Yuji 所解释的,在大多数情况下,它们是等价的,如下所示:
>>> from django.db import connection
>>> samples1 = Unit.objects.filter(color="orange", volume=None)
>>> samples2 = Unit.objects.filter(color="orange").filter(volume=None)
>>> list(samples1)
[]
>>> list(samples2)
[]
>>> for q in connection.queries:
... print q['sql']
...
SELECT `samples_unit`.`id`, `samples_unit`.`color`, `samples_unit`.`volume` FROM `samples_unit` WHERE (`samples_unit`.`color` = orange AND `samples_unit`.`volume` IS NULL)
SELECT `samples_unit`.`id`, `samples_unit`.`color`, `samples_unit`.`volume` FROM `samples_unit` WHERE (`samples_unit`.`color` = orange AND `samples_unit`.`volume` IS NULL)
>>>
【讨论】:
【参考方案3】:当您对相关对象提出请求时会有所不同, 例如
class Book(models.Model):
author = models.ForeignKey(Author)
name = models.ForeignKey(Region)
class Author(models.Model):
name = models.ForeignKey(Region)
请求
Author.objects.filter(book_name='name1',book_name='name2')
返回空集
请求
Author.objects.filter(book_name='name1').filter(book_name='name2')
返回同时拥有“name1”和“name2”图书的作者
详情请看 https://docs.djangoproject.com/en/dev/topics/db/queries/#s-spanning-multi-valued-relationships
【讨论】:
Author.objects.filter(book_name='name1',book_name='name2')
甚至不是有效的python,它将是SyntaxError: keyword argument repeated
book_name 的确切定义在哪里?你是说 book_set__name 吗?【参考方案4】:
正如您在生成的 SQL 语句中看到的那样,区别不是某些人可能怀疑的“或”。这就是 WHERE 和 JOIN 的放置方式。
Example1(同一个连接表):来自https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships
Blog.objects.filter(
entry__headline__contains='Lennon',
entry__pub_date__year=2008)
这将为您提供所有具有 一个 条目且带有 (entry__headline__contains='Lennon') AND (entry__pub_date__year=2008)
的博客,这是您期望从该查询中得到的。
结果:
Blog with entry.headline: 'Life of Lennon', entry.pub_date: '2008'
示例 2(链式)
Blog.objects.filter(
entry__headline__contains='Lennon'
).filter(
entry__pub_date__year=2008)
这将涵盖示例 1 的所有结果,但会产生更多的结果。因为它首先用(entry__headline__contains='Lennon')
过滤所有博客,然后从结果过滤(entry__pub_date__year=2008)
。
不同之处在于它还会为您提供如下结果:
一个包含多个条目的博客
entry.headline: '**Lennon**', entry.pub_date: 2000,
entry.headline: 'Bill', entry.pub_date: **2008**
在评估第一个过滤器时,由于第一个条目(即使它有其他不匹配的条目)而包含该书。当评估第二个过滤器时,由于第二个条目,该书被包括在内。
一个表:但是如果查询不涉及连接表,例如 Yuji 和 DTing 中的示例。结果是一样的。
【讨论】:
我假设我今天早上很密集,但这句话让我感到困惑:“因为它首先过滤所有带有(entry__headline__contains='Lennon')
的博客,然后从结果过滤(entry__pub_date__year=2008)
”如果“那么从结果”是准确的,为什么它会包含 entry.headline == 'Bill'
的内容...entry__headline__contains='Lennon'
不会过滤掉 Bill
实例?
我也很困惑。似乎这个答案是错误的,但它有 37 票...
此答案具有误导性和混淆性,请注意,上述内容仅在使用 Yuji 的答案中所述的 M2M 关系进行过滤时才是正确的。关键是该示例是使用每个过滤器语句过滤博客项目,而不是条目项目。
因为每个博客可能有多个条目。语言是正确的。如果您不牢记所有移动部分,这个概念可能会令人困惑。
@DustinWyatt 我也有和你一样的问题,但我终于明白了!请参阅本页下方 Grijesh Chauhan 编写的 Employee and Dependent 示例,您也会得到它。【参考方案5】:
“multiple arguments filter-query”的结果与“chained-filter-query”不同的情况,如下:
根据引用对象和关系选择被引用对象是一对多(或多对多)的。
多个过滤器:
Referenced.filter(referencing1_a=x, referencing1_b=y) # same referencing model ^^ ^^
链式过滤器:
Referenced.filter(referencing1_a=x).filter(referencing1_b=y)
两个查询可以输出不同的结果: 如果多于一个 引用模型
Referencing1
中的行可以引用同一行 参考模型Referenced
。Referenced
中可能是这种情况:Referencing1
有 1:N(一对多)或 N:M(多对多) 关系。
例子:
考虑我的应用程序my_company
有两个模型Employee
和Dependent
。 my_company
中的员工可以拥有多个受抚养人(换句话说,受抚养人可以是单个员工的儿子/女儿,而员工可以拥有多个儿子/女儿)。嗯,假设像夫妻一样都不能在my_company
工作。我以 1:m 为例
所以,Employee
是被引用模型,可以被更多引用的模型 Dependent
是引用模型。现在考虑如下关系状态:
Employee: Dependent: +------+ +------+--------+-------------+--------------+ | name | | name | E-name | school_mark | college_mark | +------+ +------+--------+-------------+--------------+ | A | | a1 | A | 79 | 81 | | B | | b1 | B | 80 | 60 | +------+ | b2 | B | 68 | 86 | +------+--------+-------------+--------------+
Dependent
a1
指employeeA
,dependentb1, b2
referencesemployeeB
。
现在我的查询是:
发现所有有儿子/女儿的员工在大学和学校都有区别标记(例如 >= 75%)?
>>> Employee.objects.filter(dependent__school_mark__gte=75,
... dependent__college_mark__gte=75)
[<Employee: A>]
输出是“A”依赖于“a1”在大学和学校都有区分标记依赖于员工“A”。注意“B”没有被选中,因为“B”的孩子在大学和学校都没有成绩。关系代数:
员工⋈(school_mark >=75 AND college_mark>=75)依赖
其次,如果我需要查询:
查找其部分受抚养人在大学和学校获得优异成绩的所有员工?
>>> Employee.objects.filter(
... dependent__school_mark__gte=75
... ).filter(
... dependent__college_mark__gte=75)
[<Employee: A>, <Employee: B>]
这次'B'也被选中,因为'B'有两个孩子(不止一个!),一个在学校'b1'有优异成绩,另一个是在大学'b2'有优异成绩。 过滤器的顺序无关紧要,我们也可以将上面的查询写为:
>>> Employee.objects.filter(
... dependent__college_mark__gte=75
... ).filter(
... dependent__school_mark__gte=75)
[<Employee: A>, <Employee: B>]
结果是一样的!关系代数可以是:
(Employee ⋈(school_mark >=75)家属)⋈(college_mark>=75)家属
注意以下:
dq1 = Dependent.objects.filter(college_mark__gte=75, school_mark__gte=75)
dq2 = Dependent.objects.filter(college_mark__gte=75).filter(school_mark__gte=75)
输出相同的结果:[<Dependent: a1>]
我检查 Django 使用 print qd1.query
和 print qd2.query
生成的目标 SQL 查询都相同(Django 1.6)。
但在语义上两者都与 me 不同。第一个看起来像简单的部分 σ[school_mark >= 75 AND college_mark >= 75](Dependent),第二个看起来像慢速嵌套查询:σ[school_mark >= 75](σ [college_mark >= 75](依赖))。
如果需要Code @codepad
顺便说一句,它在文档@Spanning multi-valued relationships 中给出了我刚刚添加了一个示例,我认为这对新手会有帮助。
【讨论】:
感谢您的有用解释,它比文档中根本不清楚的解释要好。 最后一个关于直接过滤依赖项的标记非常有用。它表明,结果的变化肯定只有在你经历多对多关系时才会发生。如果直接查询一个表,链接过滤器就像是两次组合。 如果b2
记录不存在怎么办?在这种情况下,链式过滤器是否也会打印 B 作为输出?
@ajinzrathod 不,不会。一个简单的想法是这样的:第一个过滤器给你 2 个结果。其中包含 A 和 B(员工)。现在第二个过滤器询问,“在 A 和 B 中,给我受抚养人的大学成绩 > 75 的员工。因为他们都有受抚养人(至少 1 人)的大学成绩 > 75。它再次返回。
另外,如果有人可以回答我。这是否意味着,在反向查找中,链接只是 OR 条件?【参考方案6】:
性能差异很大。试试看。
Model.objects.filter(condition_a).filter(condition_b).filter(condition_c)
与
相比,速度出奇地慢Model.objects.filter(condition_a, condition_b, condition_c)
如Effective Django ORM中所述,
QuerySet 在内存中维护状态 链接触发克隆,复制该状态 不幸的是,QuerySet 维护着很多状态 如果可能,不要链接多个过滤器
【讨论】:
【参考方案7】:如果您最终在此页面上寻找如何使用多个链接过滤器动态构建 django 查询集,但您需要过滤器是 AND
类型而不是 OR
,请考虑使用 Q objects。
一个例子:
# First filter by type.
filters = None
if param in CARS:
objects = app.models.Car.objects
filters = Q(tire=param)
elif param in PLANES:
objects = app.models.Plane.objects
filters = Q(wing=param)
# Now filter by location.
if location == 'France':
filters = filters & Q(quay=location)
elif location == 'England':
filters = filters & Q(harbor=location)
# Finally, generate the actual queryset
queryset = objects.filter(filters)
【讨论】:
如果 if 或 elif 未通过,则过滤器变量将为 None,然后您会收到 TypeError: unsupported operand type(s) for &: 'NoneType' and 'Q'。我用 filters = Q() 启动了过滤器【参考方案8】:如果需要 a 和 b 那么
and_query_set = Model.objects.filter(a=a, b=b)
如果需要 a 和 b 则
chaied_query_set = Model.objects.filter(a=a).filter(b=b)
官方文件: https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships
相关帖子:Chaining multiple filter() in Django, is this a bug?
【讨论】:
【参考方案9】:此答案基于 Django 3.1。
环境
型号
class Blog(models.Model):
blog_id = models.CharField()
class Post(models.Model):
blog_id = models.ForeignKeyField(Blog)
title = models.CharField()
pub_year = models.CharField() # Don't use CharField for date in production =]
数据库表
过滤器调用
Blog.objects.filter(post__title="Title A", post__pub_year="2020")
# Result: <QuerySet [<Blog: 1>]>
Blog.objects.filter(post__title="Title A").filter(post_pub_date="2020)
# Result: <QuerySet [<Blog: 1>, [<Blog: 2>]>
说明
在我进一步开始之前,我必须注意到这个答案是基于使用“ManyToManyField”或反向“ForeignKey”过滤对象的情况。
如果您使用同一张表或“OneToOneField”来过滤对象,那么使用“多参数过滤器”或“过滤器链”将没有区别。它们都将像“AND”条件过滤器一样工作。
了解如何使用“多参数过滤器”和“过滤器链”的直接方法是记住在“ManyToManyField”或反向“ForeignKey”过滤器中,“多参数过滤器”是一个“AND”条件而“Filter-chain”是一个“OR”条件。
“Multiple Arguments Filter”和“Filter-chain”之所以如此不同,是因为它们从不同的连接表中获取结果并在查询语句中使用不同的条件。
“多参数过滤器”使用 "Post"."Public_Year" = '2020' 来识别公共年份
SELECT *
FROM "Book"
INNER JOIN ("Post" ON "Book"."id" = "Post"."book_id")
WHERE "Post"."Title" = 'Title A'
AND "Post"."Public_Year" = '2020'
"Filter-chain" 数据库查询使用 "T1"."Public_Year" = '2020' 来识别公共年份
SELECT *
FROM "Book"
INNER JOIN "Post" ON ("Book"."id" = "Post"."book_id")
INNER JOIN "Post" T1 ON ("Book"."id" = "T1"."book_id")
WHERE "Post"."Title" = 'Title A'
AND "T1"."Public_Year" = '2020'
但为什么不同的条件会影响结果呢?
我相信大多数来到这个页面的人,包括我 =],在最初使用“多参数过滤器”和“过滤器链”时都有相同的假设。
我们认为应该从如下表中获取结果,该表对于“多参数过滤器”是正确的。所以如果你使用“Multiple Arguments Filter”,你会得到你期望的结果。
但是在处理“过滤器链”时,Django 创建了一个不同的查询语句,它将上面的表格更改为下面的表格。此外,由于查询语句的更改,“公共年份”被标识在“T1”部分而不是“发布”部分下。
但是这个奇怪的“过滤链”连接表图是从哪里来的呢?
我不是数据库专家。下面的解释是我在创建了相同的数据库结构并使用相同的查询语句进行测试后所理解的。
下图将展示这个奇怪的“过滤链”连接表图是如何来的。
数据库会先将“Blog”和“Post”表的行一一匹配,创建一个连接表。
之后,数据库现在再次执行相同的匹配过程,但使用第 1 步的结果表来匹配“T1”表,该表与“Post”表相同。
这就是这个奇怪的“过滤链”连接表图的来源。
结论
所以有两件事让“多参数过滤器”和“过滤器链”不同。
-
Django 为“Multiple Arguments Filter”和“Filter-chain”创建不同的查询语句,这使得“Multiple Arguments Filter”和“Filter-chain”的结果来自不同的表。
“Filter-chain”查询语句从与“Multiple Arguments Filter”不同的位置识别条件。
记住如何使用它的脏方法是“Multiple Arguments Filter”是一个“AND”条件,“Filter-chain”是一个“OR”条件在“ManyToManyField”或反向“ForeignKey”过滤器中。
【讨论】:
以上是关于django中具有多个参数的过滤器和链式过滤器之间的区别的主要内容,如果未能解决你的问题,请参考以下文章