1

Django での Q オブジェクトの否定に関して、非常に奇妙な問題が発生しています。例としてサッカーを使用しましょう。

class Team(models.Model):
    id = UUIDField(primary_key=True)

class Player(models.Model):
    id = UUIDField(primary_key=True)
    name = models.CharField(max_length=128)
    team = models.ForeignKey(Team)
    touchdowns = models.IntegerField()

10チームあります。

プレイヤーは 100 人で、各チームには 10 人が所属しています。各チームには「ジョー」という名前のプレーヤーがいます。5回のタッチダウンを記録した1つのチームに1人の「ジョー」がいます。他のすべてのジョーは 1 回のタッチダウンを記録しています。すべてのプレーヤーが 1 回のタッチダウンしか記録していないチームが 8 つあります。

少なくとも 3 回のタッチダウンを記録した Joe という名前のプレーヤーがいるチームを取得したいと考えています。

models.Team.objects.filter(Q(player__name="Joe", player__touchdowns__gte=3)).count()

これは当然のことながら 1 を返します。それを否定すると 9 が返されます (少なくとも 3 回のタッチダウンを行った Joe という名前のプレーヤーがいない他の 9 チーム):

models.Team.objects.filter(~Q(player__name="Joe", player__touchdowns__gte=3)).count()

代わりに、そのチームの全員が 3 タッチダウン (8) 未満のチームを返します。

どこが間違っていますか?これの実際の適用ははるかに複雑であるため、Q オブジェクトを否定と共に使用する必要があることに注意してください。Exclude は使用できません。

4

2 に答える 2

2

これらの違いが発生する理由を突き止める最良の方法は、生成されたクエリを調査することです。Django クエリセット API の使用後にデータベースに送信された実際のクエリを出力django-debug-toolbarするコマンドが付属しています。debugsqlshellこれらのテストでは、Userモデルを結合して使用しましたGroup。私も、選択したオブジェクトのカウントが異なることに気付きました。そのため、一見すると、ユースケースとの相関関係が良好であるように見えます。

User.objects.filter(~Q(username='jdoe', groups__name='Awesome Group'))

SELECT "auth_user"."id",
       "auth_user"."username",
       "auth_user"."first_name",
       "auth_user"."last_name",
       "auth_user"."email",
       "auth_user"."password",
       "auth_user"."is_staff",
       "auth_user"."is_active",
       "auth_user"."is_superuser",
       "auth_user"."last_login",
       "auth_user"."date_joined"
FROM "auth_user"
WHERE NOT ("auth_user"."username" = 'jdoe'
           AND "auth_user"."id" IN
             (SELECT U1."user_id"
              FROM "auth_user_groups" U1
              INNER JOIN "auth_group" U2 ON (U1."group_id" = U2."id")
              WHERE (U2."name" = 'Awesome Group'
                     AND U1."user_id" IS NOT NULL))) LIMIT 21

User.objects.exclude(Q(username='jdoe', groups__name='Awesome Group'))

SELECT "auth_user"."id",
       "auth_user"."username",
       "auth_user"."first_name",
       "auth_user"."last_name",
       "auth_user"."email",
       "auth_user"."password",
       "auth_user"."is_staff",
       "auth_user"."is_active",
       "auth_user"."is_superuser",
       "auth_user"."last_login",
       "auth_user"."date_joined"
FROM "auth_user"
INNER JOIN "auth_user_groups" ON ("auth_user"."id" = "auth_user_groups"."user_id")
INNER JOIN "auth_group" ON ("auth_user_groups"."group_id" = "auth_group"."id")
WHERE NOT (("auth_user"."username" = 'jdoe'
            AND "auth_group"."name" = 'Awesome Group')) LIMIT 21

ここでの違いは、INNER JOIN が発生する場所にあります。Qオブジェクトは、最初の例で INNER JOIN を引き起こし、その後、INNER JOIN による選択~. の場合exclude、否定はINNER JOINと並行して発生します。

于 2012-08-03T19:31:30.367 に答える
0

Q および ~Q を使用してデータベース内のクエリをチェックします。

>>> print models.Team.objects.filter(Q(player__name="Joe", player__touchdowns__gte=3)).query
>>> print models.Team.objects.filter(~Q(player__name="Joe", player__touchdowns__gte=3)).query

2 つの Q オブジェクトでテストします。

>>> print models.Team.objects.filter(Q(player__name="Joe") & Q(player__touchdowns__gte=3)).query
>>> print models.Team.objects.filter(~Q(player__name="Joe") & ~Q(player__touchdowns__gte=3)).query
于 2012-08-03T19:33:13.497 に答える