django中具有多个参数的过滤器和链过滤器之间的区别

tes*_*ile 65 python django django-models

django中有多个参数的过滤器和链过滤器有什么区别?

小智 46

正如您在生成的SQL语句中看到的那样,差异不是某些人可能怀疑的"OR".这是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)
Run Code Online (Sandbox Code Playgroud)

这将为您提供具有两个条目的所有博客(entry__headline__contains='Lennon') AND (entry__pub_date__year=2008),这是您对此查询的期望.

结果:

Blog with {entry.headline: 'Life of Lennon', entry.pub_date: '2008'}
Run Code Online (Sandbox Code Playgroud)

例2(链式)

Blog.objects.filter(
       entry__headline__contains='Lennon'
           ).filter(
       entry__pub_date__year=2008)
Run Code Online (Sandbox Code Playgroud)

这将涵盖示例1的所有结果,但会产生稍多的结果.因为它首先使用(entry__headline__contains='Lennon')结果过滤器过滤所有博客(entry__pub_date__year=2008).

不同之处在于它还会为您提供如下结果:

一个包含多个条目的博客

{entry.headline: '**Lennon**', entry.pub_date: 2000}, 
{entry.headline: 'Bill', entry.pub_date: **2008**}
Run Code Online (Sandbox Code Playgroud)

当评估第一个过滤器时,由于第一个条目(包括其他条目不匹配),因此包含该书.当评估第二个过滤器时,由于第二个条目,包括书.

一个表:但是如果查询不涉及连接表,如Yuji和DTing的示例.结果是一样的.

  • 我想我今天早上只是密集,但这句话让我感到困惑:"因为它首先使用`(entry__headline__contains ='Lennon')过滤所有博客,然后从结果过滤器`(entry__pub_date__year = 2008)`"If"那么从结果"是准确的,为什么它会包含一些东西与`entry.headline =='Bill'` ...不会`entry__headline__contains ='Lennon'`过滤出`Bill`实例? (15认同)
  • 我也很困惑.似乎这个答案是错的,但它有37个赞成... (3认同)
  • 此答案具有误导性和混淆性,请注意,上述内容仅在使用 Yuji 的答案中所述的 M2M 关系进行过滤时才是正确的。关键点是该示例使用每个过滤器语句过滤博客项目,而不是条目项目。 (2认同)

Gri*_*han 19

"多参数过滤查询"结果与"链式过滤查询"不同的情况如下:

在引用对象和关系的基础上选择引用的对象是一对多(或多对多).

多个过滤器:

    Referenced.filter(referencing1_a=x, referencing1_b=y)
    #  same referencing model   ^^                ^^
Run Code Online (Sandbox Code Playgroud)

链式过滤器:

    Referenced.filter(referencing1_a=x).filter(referencing1_b=y)
Run Code Online (Sandbox Code Playgroud)

两个查询都可以输出不同的结果:
如果引用模型中的多行可以引用引用模型Referencing1中的同一行Referenced.这可能是这样的情况Referenced: Referencing1具有1:N(一对多)或N:M(多对多)关系.

例:

考虑我的应用程序my_company有两个模型EmployeeDependent.一名雇员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 |
                +------+--------+-------------+--------------+  
Run Code Online (Sandbox Code Playgroud)

从属a1是指员工Ab1, b2对员工的依赖性引用B.

现在我的查询是:

找到所有有儿子/女儿的员工在大学和学校都有区别标记(比如> = 75%)?

>>> Employee.objects.filter(dependent__school_mark__gte=75,
...                         dependent__college_mark__gte=75)

[<Employee: A>]
Run Code Online (Sandbox Code Playgroud)

输出是'A'依赖'a1'在大学和学校都有区别标记依赖于员工'A'.注意'B'未被选中,因为'B'的孩子在大学和学校都有区别标记.关系代数:

雇员(school_mark> = 75和college_mark> = 75)相关

在第二种情况下,我需要一个查询:

找到所有在大学和学校都有一些家属的员工?

>>> Employee.objects.filter(
...             dependent__school_mark__gte=75
...                ).filter(
...             dependent__college_mark__gte=75)

[<Employee: A>, <Employee: B>]
Run Code Online (Sandbox Code Playgroud)

这次'B'也被选中,因为'B'有两个孩子(不止一个!),一个在学校'b1'有区别标记,另一个在大学'b2'有区别标记.
过滤顺序无关紧要我们也可以将上面的查询写成:

>>> Employee.objects.filter(
...             dependent__college_mark__gte=75
...                ).filter(
...             dependent__school_mark__gte=75)

[<Employee: A>, <Employee: B>]
Run Code Online (Sandbox Code Playgroud)

结果一样!关系代数可以是:

(员工(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)
Run Code Online (Sandbox Code Playgroud)

输出相同的结果: [<Dependent: a1>]

我检查Django使用生成的目标SQL查询print qd1.query,print qd2.query两者都相同(Django 1.6).

但语义上两者都与不同.首先看起来像简单的部分σ [school_mark> = 75 AND college_mark> = 75](依赖),第二个看起来像慢嵌套查询:σ [school_mark> = 75][college_mark> = 75](依赖)).

如果需要Code @codepad

顺便说一句,它是在文档中给出的@ Spanning多值关系我刚刚添加了一个例子,我认为这对新的人有用.

  • 感谢您提供这个有用的解释,它比文档中的解释要好,但文档中根本不清楚。 (6认同)
  • 关于直接过滤依赖项的最后一个标记非常有用。它表明结果的变化只有在您经历多对多关系时才会发生。如果直接查询一个表,链接过滤器就像两次梳理一样。 (2认同)

Yuj*_*ita 16

大多数情况下,查询只有一组可能的结果.

当您处理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))
Run Code Online (Sandbox Code Playgroud)

欢迎其他例子.

  • 另一个例子:它不仅限于m2m,这也可能发生在一对多 - 反向查找,例如在ForeignKey上使用related_name (3认同)

tar*_*tar 9

性能差异巨大.试试看吧.

Model.objects.filter(condition_a).filter(condition_b).filter(condition_c)

与...相比,这是惊人的缓慢

Model.objects.filter(condition_a, condition_b, condition_c)

"Effective Django ORM"中所述,

  • QuerySets在内存中维护状态
  • 链接触发克隆,复制该状态
  • 不幸的是,QuerySets保持了很多状态
  • 如果可能,请勿链接多个过滤器


Lea*_*arn 8

这个答案基于 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 =]
Run Code Online (Sandbox Code Playgroud)

数据库表

在此处输入图片说明

过滤器调用

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>]>
Run Code Online (Sandbox Code Playgroud)

解释

在我进一步开始之前,我必须注意到这个答案是基于使用“ManyToManyField”或反向“ForeignKey”来过滤对象的情况。

如果您使用同一个表或“OneToOneField”来过滤对象,那么使用“Multiple Arguments Filter”或“Filter-chain”将没有区别。它们都将像“AND”条件过滤器一样工作。

了解如何使用“Multiple Arguments Filter”和“Filter-chain”的直接方法是记住在“ManyToManyField”或反向“ForeignKey”过滤器中,“Multiple Arguments Filter”是“AND”条件和“Filter” -chain”是一个“或”条件。

“Multiple Arguments Filter”和“Filter-chain”之所以如此不同,是因为它们从不同的连接表中获取结果并在查询语句中使用不同的条件。

"Multiple Arguments Filter" 使用"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'
Run Code Online (Sandbox Code Playgroud)

"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'
Run Code Online (Sandbox Code Playgroud)

但是为什么不同的条件会影响结果呢?

我相信我们大多数来到这个页面的人,包括我 =],在使用“多参数过滤器”和“过滤器链”时都有相同的假设。

我们认为应该从如下表中获取结果,该表对“多参数过滤器”是正确的。因此,如果您使用“Multiple Arguments Filter”,您将获得预期的结果。

在此处输入图片说明

但是在处理“过滤器链”时,Django 创建了一个不同的查询语句,将上表更改为下一个。此外,由于查询语句的更改,“公共年”被标识在“T1”部分而不是“发布”部分下。

在此处输入图片说明

但是这个奇怪的“过滤链”连接表图从何而来?

我不是数据库专家。下面的解释是我创建数据库的相同结构并使用相同的查询语句进行测试后到目前为止我所理解的。

下图将展示这个奇怪的“过滤器链”连接表图是如何来的。

在此处输入图片说明

在此处输入图片说明

数据库首先会通过将“Blog”和“Post”表的行一一匹配来创建联接表。

之后,数据库现在再次执行相同的匹配过程,但使用步骤 1 的结果表来匹配“T1”表,该表与“Post”表相同。

这就是这个奇怪的“过滤器链”连接表图的来源。

结论

所以有两件事使“多参数过滤器”和“过滤器链”不同。

  1. Django 为“Multiple Arguments Filter”和“Filter-chain”创建不同的查询语句,这使得“Multiple Arguments Filter”和“Filter-chain”结果来自不同的表。
  2. “过滤器链”查询语句从与“多参数过滤器”不同的地方识别条件。

记住如何使用它的肮脏方法是“多参数过滤器”是“与”条件,而“过滤器链”是“多对多字段”或反向“外键”过滤器中的“或”条件。


DTi*_*ing 6

您可以使用连接模块查看要比较的原始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)
>>> 
Run Code Online (Sandbox Code Playgroud)