複数の引数を持つフィルターとdjangoのチェーンフィルターの違い


回答:


60

生成されたSQLステートメントでわかるように、一部の人が疑うように、違いは「OR」ではありません。これは、WHEREとJOINの配置方法です。

例1(同じ結合テーブル):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)

これにより、両方のエントリが1つあるすべてのブログ(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**}

最初のフィルターが評価されたとき、最初のエントリのために本が含まれています(一致しない他のエントリがある場合でも)。2番目のフィルターが評価されると、2番目のエントリのために本が含まれます。

1つのテーブル:ただし、クエリにYujiやDTingの例のような結合テーブルが含まれていない場合。結果は同じです。


21
今朝は密集していると思いますが、この文は私を混乱させます。「最初にすべてのブログをフィルタリングし(entry__headline__contains='Lennon')、次に結果フィルターからフィルタリング(entry__pub_date__year=2008)するため」「その後、結果から」が正確である場合、なぜentry.headline == 'Bill'.. .wouldn'tentry__headline__contains='Lennon'からフィルタをBillインスタンス?
ダスティンワイアット

7
私も混乱しています。この答えは間違っているようですが、37の賛成票があります...
Personman 2018

1
この回答は誤解を招き、混乱を招きます。上記は、Yujiの回答に記載されているように、M2M関係を使用してフィルタリングする場合にのみ正しいことに注意してください。重要な点は、この例では、エントリアイテムではなく、各フィルタステートメントでブログアイテムをフィルタリングすることです。
アナウンサー2018年

1
ブログごとに複数のエントリがある可能性があるためです。言語は正しいです。すべての可動部分を念頭に置いていない場合、概念は混乱する可能性があります。
DylanYoung 2018年

@DustinWyatt私もあなたと同じ質問をしましたが、ついにそれを手に入れました!このページの下のGrijeshChauhanによって書かれた従業員と扶養家族の例を参照してください。あなたもそれを手に入れるでしょう。
theQuestionMan

33

「複数引数filter-query」の結果が「chained-filter-query」と異なる場合は、次のようになります。

参照オブジェクトと関係に基づいて参照オブジェクトを選択することは、1対多(または多対多)です。

複数のフィルター:

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

連鎖フィルター:

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

両方のクエリで異なる結果が出力される可能性があります。refering
-modelの複数の行がreferred-modelのReferencing1同じ行を参照できる場合Referenced。これは場合であってもよいReferencedReferencing1いずれか1持っている:N(多くの1つ)、またはN:M(多くの多くの)関係船。

例:

私のアプリケーションにmy_companyは2つのモデルEmployeeとがありDependentます。の従業員my_companyは複数の扶養家族を持つことができます(言い換えると、扶養家族は1人の従業員の息子/娘であり、従業員は複数の息子/娘を持つことができます)。
ええと、夫婦のように両方ともで働くことができないと仮定し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> = 75 AND College_mark> = 75)扶養家族

次に、クエリが必要な場合:

一部の扶養家族が大学と学校で区別マークを持っているすべての従業員を見つけますか?

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

[<Employee: A>, <Employee: B>]

今回は、「B」にも2人の子供(複数!)がいて、1人は学校「b1」に区別マークがあり、もう1人は大学「b2」に区別マークがあるため、「B」も選択されました。
フィルタの順序は重要ではありません。上記のクエリを次のように記述することもできます。

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

[<Employee: A>, <Employee: B>]

結果は同じです!関係代数は次のようになります。

(従業員(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によって生成されたターゲットSQLクエリを確認しましたがprint qd1.queryprint qd2.queryどちらも同じです(Django1.6)。

しかし、意味的には両方とも私とは異なります。最初は単純なセクションσ [school_mark> = 75 AND College_mark> = 75](依存)のように見え、2番目は遅いネストされたクエリのように見えます:σ [school_mark> = 75](σ [college_mark> = 75](依存))。

コード@codepadが必要な場合

ところで、それはドキュメントに記載されています@スパニング多値関係例を追加しました。新しい人に役立つと思います。


4
この有益な説明をありがとう、それはまったく明確ではないドキュメントのものよりも優れています。
2014年

1
扶養家族を直接フィルタリングすることについての最後のマークは非常に役に立ちます。結果の変化は、多対多の関係を経たときにのみ確実に発生することを示しています。テーブルを直接クエリする場合、フィルターのチェーンは2回コーミングするのと同じです。
クリス

20

ほとんどの場合、クエリの結果の可能なセットは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))

他の例も歓迎します。


4
別の例:それはちょうど、M2Mに制限されていない、これはまた、1対多で発生することができます-のForeignKeyにrelated_nameを使用して例えば、ルックアップ逆に
WIM

説明ありがとうございます!その前は、最後の例と2番目の例が同じだと思っていたので、最後の例はうまくいきませんでした(クエリ結果が間違っていました)。検索に多くの時間を費やしました。私にとって非常に役立つ2番目の例。また、ウィムが言ったように、これは私の場合のように逆の1対多の関係で使用できます。
zen11625 2016

12

パフォーマンスの違いは非常に大きいです。試してみてください。

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

に比べて驚くほど遅い

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

効果的なDjangoORMで述べたように、

  • QuerySetsはメモリ内の状態を維持します
  • チェーンはクローン作成をトリガーし、その状態を複製します
  • 残念ながら、QuerySetsは多くの状態を維持します
  • 可能であれば、複数のフィルターをチェーンしないでください

8

接続モジュールを使用して、比較する生のSQLクエリを確認できます。Yuji'sが説明しているように、ほとんどの場合、ここに示すように同等です。

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

この回答はDjango3.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」を使用してオブジェクトをフィルタリングしている場合、「MultipleArgumentsFilter」と「Filter-chain」のどちらを使用しても違いはありません。どちらも「AND」条件フィルターのように機能します。

「MultipleArgumentsFilter」と「Filter-chain」の使用方法を理解する簡単な方法は、「ManyToManyField」または逆の「ForeignKey」フィルターで覚えておくことです。「MultipleArguments Filter」は「AND」条件であり、「Filter」 -chain "は" OR "条件です。

「MultipleArgumentsFilter」と「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'

「フィルターチェーン」データベースクエリは、「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'

しかし、なぜ異なる条件が結果に影響を与えるのでしょうか?

私=]を含め、このページにアクセスするほとんどの人は、最初は「複数引数フィルター」と「フィルターチェーン」を使用している間、同じ仮定を持っていると思います。

これは、「複数引数フィルター」に適した次のようなテーブルから結果をフェッチする必要があると考えています。したがって、「複数引数フィルター」を使用している場合は、期待どおりの結果が得られます。

ここに画像の説明を入力してください

しかし、「フィルターチェーン」を処理している間、Djangoは上記のテーブルを次のテーブルに変更する別のクエリステートメントを作成します。また、クエリステートメントが変更されたため、「公開年」は「投稿」セクションではなく「T1」セクションで識別されます。

ここに画像の説明を入力してください

しかし、この奇妙な「フィルターチェーン」結合テーブル図はどこから来たのでしょうか。

私はデータベースの専門家ではありません。以下の説明は、データベースの同じ構造を作成し、同じクエリステートメントでテストを行った後のこれまでの理解です。

次の図は、この奇妙な「フィルターチェーン」結合テーブル図がどのように作成されているかを示しています。

ここに画像の説明を入力してください

ここに画像の説明を入力してください

データベースは最初に、「ブログ」テーブルと「投稿」テーブルの行を1つずつ照合することにより、結合テーブルを作成します。

その後、データベースは同じ照合プロセスを再度実行しますが、ステップ1の結果テーブルを使用して、同じ「Post」テーブルである「T1」テーブルを照合します。

そして、これがこの奇妙な「フィルターチェーン」結合テーブル図の由来です。

結論

したがって、「複数引数フィルター」と「フィルターチェーン」は2つの点で異なります。

  1. Djangoは、「MultipleArgumentsFilter」と「Filter-chain」に対して異なるクエリステートメントを作成し、「MultipleArgumentsFilter」と「Filter-chain」の結果を異なるテーブルから取得します。
  2. 「Filter-chain」クエリステートメントは、「MultipleArgumentsFilter」とは異なる場所から条件を識別します。

それを使用する方法を覚える汚い方法は、「Multiple Arguments Filter」は「AND」条件であり、「Filter-chain」は「ManyToManyField」または逆の「ForeignKey」フィルターにあるときの「OR」条件です。


2

このページで、複数のチェーンフィルターを使用してdjangoクエリセットを動的に構築する方法を探しているが、フィルターをではANDなくタイプにする必要がある場合は、QオブジェクトのOR使用を検討してください。

例:

# 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が渡されない場合、filters変数はNoneになり、&: 'NoneType'および 'Q'に対してTypeError:unsupportedオペランドtype(s)を受け取ります。私は、フィルタ= Q()でフィルタを開始
cwhisperer


-4

たとえば、関連するオブジェクトにリクエストがある場合は違いがあります

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をご覧ください。


5
Author.objects.filter(book_name='name1',book_name='name2')有効なPythonでさえありません、それはそうなるでしょうSyntaxError: keyword argument repeated
2014年

1
book_nameはどこで正確に定義されていますか?book_set__nameのことですか?
DylanYoung 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.