封装过滤器时出错(Q)

时间:2012-01-26 18:16:13

标签: django django-models django-queryset django-q

如果我有下一个代码:

class A(models.Model):

   .....

class B(models.Model):

    a = models.ManyToManyField(A)

接下来的查询会得到不同的结果:

B.objects.exclude(a__in=[7])

from django.db.models import Q
B.objects.exclude(Q(a__in=[7]))

结果:

  • 第一个查询获取除“b对象”之外的所有对象,其中a = 7。没关系
  • 但第二个查询获取除“b对象”之外的所有对象,其中a = 7或a = None。

这是一个错误吗?,它是否已知?

我添加了一个详细的例子,执行下一个代码

from django.contrib.auth.models import User, Group
u1 = User.objects.create(username='u1')
u2 = User.objects.create(username='u2')
u3 = User.objects.create(username='u3')
g1 = Group.objects.create(name='g1')
g2 = Group.objects.create(name='g2')
u1.groups.add(g1)
u2.groups.add(g2)
print User.objects.exclude(groups__in=[g1.pk])
print User.objects.exclude(Q(groups__in=[g1.pk]))

2 个答案:

答案 0 :(得分:2)

我不确定我称之为“错误”,但这两个版本确实发送了独特的查询(Django 1.3.1)。

没有Q

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"."id" IN
             (SELECT U1."user_id"
              FROM "auth_user_groups" U1
              WHERE (U1."group_id" IN (2)
                     AND U1."user_id" IS NOT NULL)))

使用Q

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")
WHERE NOT ("auth_user_groups"."group_id" IN (2))

有趣的是,如果您使用filter而不是exclude,它们都会发送完全相同的查询。然而,这实际上可能是故意的。 Q本身从未真正使用过(没有意义),因此查询可能已针对其他AND / OR / NOT关系进行了预优化。然而,出于所有意图和目的,没有Q的版本已经完成。如果你确实遇到这种行为的问题,你可以提交一张票,但我只是说你只有Q才能使用它。

答案 1 :(得分:1)

现在这已在Django中修复:

https://code.djangoproject.com/ticket/17600

这是一个django bug