2

オブジェクトを使用してクエリセットを作成するときに問題がありQます。いくつかの条件をどのように注文するかによって、異なる結果が得られQます。問題をきれいに説明するために、モデルを少し単純化します。

class D(models.Model):
    one_attr = models.BooleanField()
    s_attr = models.ManyToManyField(S, through='DRelatedToS')
    d_to_p = models.ForeignKey(P)


class S(models.Model):
    other_attr = models.BooleanField()
    s_to_p = models.ForeignKey(P)


class DRelatedToS(models.Model):
    to_s = models.ForeignKey(S)
    to_d = models.ForeignKey(D)
    date = models.DateField()


class P(models.Model):
    the_last_attr = models.PositiveIntegerField()

関係の要約:

D <-- DRelatedToS --> S --> P
|                           ^
|                           |
-------->------->------>----^

これらのモデルと関係を使用すると、Q 条件をどのように配置するかによって、2 つの異なる結果が得られ ます。最初のクエリで 1 つの結果が得られます。

D.objects.filter(
    Q(one_attr=True, s_attr__s_to_p__the_last_attr=5)
    |
    Q(one_attr=False, d_to_p__the_last_attr=10)
)

最初のクエリとは異なる別の結果を与える 2 番目のクエリ

D.objects.filter(
    Q(one_attr=False, d_to_p__the_last_attr=10)
    |
    Q(one_attr=True, s_attr__s_to_p__the_last_attr=5)
)

私の質問は、なぜこれが起こっているのですか? クエリの実行方法に問題はありますか?

LEFT OUTER JOINこれらのクエリから派生したSQL ステートメントを見ると、2 つの異なるステートメントが得られINNER JOINますINNER JOIN。私が欲しいものを実際に返すものは、LEFT OUTER JOIN. これにより、条件をどのように調整するかによって、すべてのクエリが悪い結果を返す可能性があると感じます。これはバグですか、それとも何か (またはすべて) 間違っていますか?

4

3 に答える 3

3

あなたの例では、異なる順序で同じ結果が返されるはずです。

それにもかかわらず、(質問のコードで行った修正を使用して) コードをテストしましたが、説明したエラーを生成できません。コードを単純化したときに他のエラーを導入して見逃した可能性があります。使用したサンプルデータを投稿していただけますか? (以下の私のデータを参照してください)。

まず、サンプルコードにバグがあります。質問を編集して、問題を修正し、テスト用に簡素化および改善するための次の修正を提案しましたが、変更が更新されていないため、ここで繰り返します。

訂正 1: diff 形式でのモデル変更:

3,4c6,10
<     s_attr = models.ManyToMany(S, through='DRelatedToS')
<     d_to_p = models.ForeignKey(P)
---
>     s_attr = models.ManyToManyField('S', through='DRelatedToS')
>     d_to_p = models.ForeignKey('P')
> 
>     def __unicode__(self):
>         return 'D:(%s,%s,%s)' % (self.id, self.one_attr, self.d_to_p.the_last_attr)
8,9c14
<     other_attr = models.BooleanField()
<     s_to_p = models.ForeignKey(P)
---
>     s_to_p = models.ForeignKey('P')
13d17
<     to_p = models.ForeignKey(P)
15c19
<     date = models.DateField()
---
>     to_s = models.ForeignKey(S)
19a24
> 

したがって、修正を適用すると、モデルは次のようになります。

class D(models.Model):
    one_attr = models.BooleanField()
    s_attr = models.ManyToManyField('S', through='DRelatedToS')
    d_to_p = models.ForeignKey('P')

    def __unicode__(self):
        return 'D:(%s,%s,%s)' % (self.id, self.one_attr, self.d_to_p.the_last_attr)


class S(models.Model):
    s_to_p = models.ForeignKey('P')


class DRelatedToS(models.Model):
    to_d = models.ForeignKey(D)
    to_s = models.ForeignKey(S)


class P(models.Model):
    the_last_attr = models.PositiveIntegerField()

訂正 2:クエリのルックアップ フィールドが間違っています (回答で修正済み)。

以下は私がしたことです:

  1. という名前のプロジェクトとアプリを作成しますtestso

    django-admin.py startproject marianobianchi
    cd marianobianchi
    python manage.py startapp testso
    
  2. testsoモデルを追加し、プロジェクト設定を調整します (データベース設定、に追加INSTALLED_APPS)

  3. サンプル データを追加します。

    mkdir testso/fixtures
    cat > testso/fixtures/initial_data.json
    [
        {"pk": 1, "model": "testso.d", "fields": {"one_attr": true, "d_to_p": 3}},
        {"pk": 2, "model": "testso.d", "fields": {"one_attr": true, "d_to_p": 4}},
        {"pk": 3, "model": "testso.d", "fields": {"one_attr": false, "d_to_p": 5}},
        {"pk": 4, "model": "testso.d", "fields": {"one_attr": false, "d_to_p": 5}},
    
        {"pk": 1, "model": "testso.s", "fields": {"s_to_p": 1}},
        {"pk": 2, "model": "testso.s", "fields": {"s_to_p": 2}},
        {"pk": 3, "model": "testso.s", "fields": {"s_to_p": 3}},
    
        {"pk": 1, "model": "testso.drelatedtos", "fields": {"to_d": 2, "to_s": 1}},
        {"pk": 2, "model": "testso.drelatedtos", "fields": {"to_d": 1, "to_s": 2}},
        {"pk": 3, "model": "testso.drelatedtos", "fields": {"to_d": 1, "to_s": 3}},
    
        {"pk": 1, "model": "testso.p", "fields": {"the_last_attr": 5}},
        {"pk": 2, "model": "testso.p", "fields": {"the_last_attr": 5}},
        {"pk": 3, "model": "testso.p", "fields": {"the_last_attr": 3}},
        {"pk": 4, "model": "testso.p", "fields": {"the_last_attr": 4}},
        {"pk": 5, "model": "testso.p", "fields": {"the_last_attr": 10}}
    ]
    
  4. python manage.py syncdb

  5. python manage.py shell
  6. シェル内:

    >>> from testso.models import *
    >>> from django.db.models import Q
    >>> D.objects.filter(Q(one_attr=True, s_attr__s_to_p__the_last_attr=5) | Q(one_attr=False, d_to_p__the_last_attr=10))
    [<D: D:(1,True,3)>, <D: D:(2,True,4)>, <D: D:(3,False,10)>, <D: D:(4,False,10)>]
    >>> D.objects.filter(Q(one_attr=False, d_to_p__the_last_attr=10) | Q(one_attr=True, s_attr__s_to_p__the_last_attr=5))
    [<D: D:(1,True,3)>, <D: D:(2,True,4)>, <D: D:(3,False,10)>, <D: D:(4,False,10)>]
    

同じ結果!...予想通り。

于 2013-11-16T16:11:18.390 に答える
2

あなたの質問に直接答えることはできませんが、より一貫した結果が得られる別の方法があるかもしれません。

subset_a = D.objects.filter(one_attr=False, d_to_p__the_last_attr=10)
subset_b = D.objects.filter(one_attr=True, s_attr__p__the_last_attr=5)
union_set = subset_a | subset_b
union_set = union_set.distinct()

| | 2 つのクエリセットの演算子はユニオンを実行し、distinct は重複した行を取得しないようにします。ここでの取り決めも同様に重要かどうかを聞いてみたいと思います.

于 2013-11-13T18:35:58.807 に答える
0

Django のORM 実装は弱いようです。「フィルター」またはその他の方法を使用してデータベースにクエリを実行し、同じ不整合が発生するかどうかを確認することをお勧めします。

または、 peeweeなどの代替 ORM 実装を探す必要があるかもしれません。

于 2013-11-13T18:09:32.843 に答える