複数の引数を持つフィルターとdjangoのチェーンフィルターの違いは何ですか?
複数の引数を持つフィルターとdjangoのチェーンフィルターの違いは何ですか?
回答:
生成された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の例のような結合テーブルが含まれていない場合。結果は同じです。
(entry__headline__contains='Lennon')
、次に結果フィルターからフィルタリング(entry__pub_date__year=2008)
するため」「その後、結果から」が正確である場合、なぜentry.headline == 'Bill'
.. .wouldn'tentry__headline__contains='Lennon'
からフィルタをBill
インスタンス?
「複数引数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
。これは場合であってもよいReferenced
。Referencing1
いずれか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.query
、print qd2.query
どちらも同じです(Django1.6)。
しかし、意味的には両方とも私とは異なります。最初は単純なセクションσ [school_mark> = 75 AND College_mark> = 75](依存)のように見え、2番目は遅いネストされたクエリのように見えます:σ [school_mark> = 75](σ [college_mark> = 75](依存))。
コード@codepadが必要な場合
ところで、それはドキュメントに記載されています@スパニング多値関係例を追加しました。新しい人に役立つと思います。
ほとんどの場合、クエリの結果の可能なセットは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))
他の例も歓迎します。
パフォーマンスの違いは非常に大きいです。試してみてください。
Model.objects.filter(condition_a).filter(condition_b).filter(condition_c)
に比べて驚くほど遅い
Model.objects.filter(condition_a, condition_b, condition_c)
効果的なDjangoORMで述べたように、
- QuerySetsはメモリ内の状態を維持します
- チェーンはクローン作成をトリガーし、その状態を複製します
- 残念ながら、QuerySetsは多くの状態を維持します
- 可能であれば、複数のフィルターをチェーンしないでください
接続モジュールを使用して、比較する生の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)
>>>
この回答は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つの点で異なります。
それを使用する方法を覚える汚い方法は、「Multiple Arguments Filter」は「AND」条件であり、「Filter-chain」は「ManyToManyField」または逆の「ForeignKey」フィルターにあるときの「OR」条件です。
このページで、複数のチェーンフィルターを使用して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)
aとbが必要な場合
and_query_set = Model.objects.filter(a=a, b=b)
bだけでなくaも必要な場合
chaied_query_set = Model.objects.filter(a=a).filter(b=b)
公式ドキュメント:https: //docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships
たとえば、関連するオブジェクトにリクエストがある場合は違いがあります
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'の両方の本を持っている著者を返します
Author.objects.filter(book_name='name1',book_name='name2')
有効なPythonでさえありません、それはそうなるでしょうSyntaxError: keyword argument repeated