difference between filter with multiple arguments and chain filter in django

前端 未结 9 754
[愿得一人]
[愿得一人] 2020-11-30 20:08

What is the difference between filter with multiple arguments and chain filter in django?

相关标签:
9条回答
  • 2020-11-30 20:35

    If you end up on this page looking for how to dynamically build up a django queryset with multiple chaining filters, but you need the filters to be of the AND type instead of OR, consider using Q objects.

    An example:

    # 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)
    
    0 讨论(0)
  • 2020-11-30 20:42

    If requires a and b then

    and_query_set = Model.objects.filter(a=a, b=b)
    

    if requires a as well as b then

    chaied_query_set = Model.objects.filter(a=a).filter(b=b)
    

    Official Documents: https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships

    Related Post: Chaining multiple filter() in Django, is this a bug?

    0 讨论(0)
  • 2020-11-30 20:47

    The case in which results of "multiple arguments filter-query" is different than "chained-filter-query", following:

    Selecting referenced objects on the basis of referencing objects and relationship is one-to-many (or many-to-many).

    Multiple filters:

        Referenced.filter(referencing1_a=x, referencing1_b=y)
        #  same referencing model   ^^                ^^
    

    Chained filters:

        Referenced.filter(referencing1_a=x).filter(referencing1_b=y)
    

    Both queries can output different result:
    If more then one rows in referencing-modelReferencing1can refer to same row in referenced-modelReferenced. This can be the case in Referenced: Referencing1 have either 1:N (one to many) or N:M (many to many) relation-ship.

    Example:

    Consider my application my_company has two models Employee and Dependent. An employee in my_company can have more than dependents(in other-words a dependent can be son/daughter of a single employee, while a employee can have more than one son/daughter).
    Ehh, assuming like husband-wife both can't work in a my_company. I took 1:m example

    So, Employee is referenced-model that can be referenced by more then Dependent that is referencing-model. Now consider relation-state as follows:

    Employee:        Dependent:
    +------+        +------+--------+-------------+--------------+
    | name |        | name | E-name | school_mark | college_mark |
    +------+        +------+--------+-------------+--------------+
    | A    |        | a1   |   A    |          79 |           81 |
    | B    |        | b1   |   B    |          80 |           60 |
    +------+        | b2   |   B    |          68 |           86 |
                    +------+--------+-------------+--------------+  
    

    Dependenta1refers to employeeA, and dependentb1, b2references to employeeB.

    Now my query is:

    Find all employees those having son/daughter has distinction marks (say >= 75%) in both college and school?

    >>> Employee.objects.filter(dependent__school_mark__gte=75,
    ...                         dependent__college_mark__gte=75)
    
    [<Employee: A>]
    

    Output is 'A' dependent 'a1' has distinction marks in both college and school is dependent on employee 'A'. Note 'B' is not selected because nether of 'B''s child has distinction marks in both college and school. Relational algebra:

    Employee (school_mark >=75 AND college_mark>=75)Dependent

    In Second, case I need a query:

    Find all employees whose some of dependents has distinction marks in college and school?

    >>> Employee.objects.filter(
    ...             dependent__school_mark__gte=75
    ...                ).filter(
    ...             dependent__college_mark__gte=75)
    
    [<Employee: A>, <Employee: B>]
    

    This time 'B' also selected because 'B' has two children (more than one!), one has distinction mark in school 'b1' and other is has distinction mark in college 'b2'.
    Order of filter doesn't matter we can also write above query as:

    >>> Employee.objects.filter(
    ...             dependent__college_mark__gte=75
    ...                ).filter(
    ...             dependent__school_mark__gte=75)
    
    [<Employee: A>, <Employee: B>]
    

    result is same! Relational algebra can be:

    (Employee (school_mark >=75)Dependent) (college_mark>=75)Dependent

    Note following:

    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)
    

    Outputs same result: [<Dependent: a1>]

    I check target SQL query generated by Django using print qd1.query and print qd2.query both are same(Django 1.6).

    But semantically both are different to me. first looks like simple section σ[school_mark >= 75 AND college_mark >= 75](Dependent) and second like slow nested query: σ[school_mark >= 75][college_mark >= 75](Dependent)).

    If one need Code @codepad

    btw, it is given in documentation @Spanning multi-valued relationships I have just added an example, I think it will be helpful for someone new.

    0 讨论(0)
  • 2020-11-30 20:51

    You can use the connection module to see the raw sql queries to compare. As explained by Yuji's, for the most part they are equivalent as shown here:

    >>> 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)
    >>> 
    
    0 讨论(0)
  • 2020-11-30 20:53

    This answer is based on Django 3.1.

    Environment

    Models

    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 =]
    

    Database tables

    Filters call

    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>]>
    

    Explanation

    Before I start anything further, I have to notice that this answer is based on the situation that uses "ManyToManyField" or a reverse "ForeignKey" to filter objects.

    If you are using the same table or an "OneToOneField" to filter objects, then there will be no difference between using "Multiple Arguments Filter" or "Filter-chain". They both will work like a "AND" condition filter.

    The straight forward way to understand how to use "Multiple Arguments Filter" and "Filter-chain" is to remember in a "ManyToManyField" or a reverse "ForeignKey" filter, "Multiple Arguments Filter" is an "AND" condition and "Filter-chain" is an "OR" condition.

    The reason that makes "Multiple Arguments Filter" and "Filter-chain" so different is because they fetch result from different join table and use different condition in the query statement.

    "Multiple Arguments Filter" use "Post"."Public_Year" = '2020' to identify the public year

    SELECT *
    FROM "Book" 
    INNER JOIN ("Post" ON "Book"."id" = "Post"."book_id")
    WHERE "Post"."Title" = 'Title A'
    AND "Post"."Public_Year" = '2020'
    

    "Filter-chain" database query use "T1"."Public_Year" = '2020' to identify the public year

    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'
    

    But why does different conditions impact the result?

    I believe most of us who come to this page, including me =], has the same assumption while using "Multiple Arguments Filter" and "Filter-chain" at first.

    Which we believe the result should be fetched from a table like following one which is correct for "Multiple Arguments Filter". So if you are using "Multiple Arguments Filter", you will get a result as your expectation.

    But while dealing with the "Filter-chain", Django creates a different query statement which changes the above table to the following one. Also, the "Public Year" is identified under the "T1" section instead of the "Post" section because of the query statement change.

    But where does this weird "Filter-chain" join table diagram come from?

    I'm not a database expert. The explanation below is what I understand so far after I created the same structure of the database and made a test with the same query statement.

    The following diagram will show how this weird "Filter-chain" join table diagram come from.

    The database will first create a join table by matching the row of "Blog" and "Post" tables one by one.

    After that, the database now does the same matching process again but using the step 1 result table to match the "T1" table which is just the same "Post" table.

    And this is how this weird "Filter-chain" join table diagram come from.

    Conclusion

    So two thing make "Multiple Arguments Filter" and "Filter-chain" different.

    1. Django create different query statements for "Multiple Arguments Filter" and "Filter-chain" which make "Multiple Arguments Filter" and "Filter-chain" result come from different tables.
    2. "Filter-chain" query statement identifies condition from a different place than "Multiple Arguments Filter".

    The dirty way to remember how to use it is "Multiple Arguments Filter" is an "AND" condition and "Filter-chain" is an "OR" condition while in a "ManyToManyField" or a reverse "ForeignKey" filter.

    0 讨论(0)
  • 2020-11-30 20:55

    Most of the time, there is only one possible set of results for a query.

    The use for chaining filters comes when you are dealing with m2m:

    Consider this:

    # 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))
    

    Other examples are welcome.

    0 讨论(0)
提交回复
热议问题