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

时间:2011-04-04 18:48:36

标签: python django django-models

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

10 个答案:

答案 0 :(得分:42)

正如您在生成的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)

这将为您提供包含(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的示例。结果是一样的。

答案 1 :(得分:15)

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

当您处理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))

欢迎其他例子。

答案 2 :(得分:13)

“多个参数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有两个模型EmployeeDependentmy_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 |
                +------+--------+-------------+--------------+  
     

依赖a1是指员工A,以及对员工b1, b2的依赖B引用。

现在我的查询是:

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

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

[<Employee: A>]

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

  

员工 (school_mark&gt; = 75 AND college_mark&gt; = 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>]

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

  

(员工 (school_mark&gt; = 75)依赖) (college_mark&gt; = 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>]

我使用print qd1.queryprint qd2.query检查Django生成的目标SQL查询是否相同(Django 1.6)。

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

如果需要Code @codepad

顺便说一句,它是在文档@ Spanning multi-valued relationships中给出的。我刚刚添加了一个示例,我认为这对新的人有用。

答案 3 :(得分:8)

性能差异很大。试试吧,看看。

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维护了很多状态
  •   
  • 如果可能,请不要链接多个过滤器
  •   

答案 4 :(得分: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)
>>> 

答案 5 :(得分:3)

此答案基于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 =]

数据库表

enter image description here

过滤通话

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”条件而“过滤链”是“或”条件。

使“多参数过滤器”和“过滤器链”如此不同的原因是,它们从不同的联接表中获取结果并在查询语句中使用不同的条件。

“多个参数过滤器”使用“发布”。“ 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'

“过滤链”数据库查询使用“ 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'

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

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

我们认为应该从一张类似于“多参数过滤器”正确的表中获取结果。因此,如果您使用“多个参数过滤器”,则会得到预期的结果。

enter image description here

但是在处理“过滤器链”时,Django创建了另一个查询语句,该语句将上表更改为下一个表。另外,由于查询语句的更改,在“ T1”部分下而不是“ Post”部分下标识了“ Public Year”。

enter image description here

但是这个奇怪的“过滤链”联接表图是从哪里来的?

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

下图将显示此奇怪的“过滤链”联接表图的来源。

enter image description here

enter image description here

数据库将首先通过将“博客”和“发布”表的行一一匹配来创建联接表。

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

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

结论

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

  1. Django为“多个参数过滤器”和“过滤器链”创建不同的查询语句,这些查询语句使“多个参数过滤器”和“过滤器链”的结果来自不同的表。
  2. “过滤器链”查询语句从“多参数过滤器”所在的其他位置识别条件。

记住使用方法的肮脏方式是“多个参数过滤器”是“ AND” 条件,而“过滤器链”是“ OR” 条件而在“ ManyToManyField”或反向“ ForeignKey”过滤器中。

答案 6 :(得分:1)

如果您最终在此页面上查找如何使用多个链接过滤器动态构建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)

答案 7 :(得分:0)

如果需要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?

答案 8 :(得分:-2)

从django.db.models导入Q

&是=布尔值(i) |是=布尔(o)

models.objects.filter(Q(id = self.kwargs ['pk'])&Q(date = self.kwargs ['date'])) models.objects.filter(Q(id = self.kwargs ['pk'])| Q(date = self.kwargs ['date']))

答案 9 :(得分:-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