queryset过滤器中使用的EmptyQuerySet上的values_list返回完整集

Ste*_*lin 6 django django-models

最近在过滤器中发现了一些奇特的东西,我无法相信它的预期行为.

from django.contrib.auth.models import User
print User.objects.filter(id__in=User.objects.none().values_list("id",flat=True))
print User.objects.filter(id__in=User.objects.all().values_list("id",flat=True))
Run Code Online (Sandbox Code Playgroud)

奇怪的是,这两个列表都返回了完整的用户集.如果我将内部查询包装在列表函数中,实际上似乎很容易"修复"

User.objects.filter(id__in=list(User.objects.none().values_list("id")))
Run Code Online (Sandbox Code Playgroud)

然后这会返回我期望的结果(空列表).

对我来说似乎是个错误,或者我错过了什么?

史蒂夫

Chr*_*att 2

以下是为两者生成的查询:

User.objects.filter(id__in=User.objects.none().values_list("id",flat=True))

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 "auth_user"."id" IN
    (SELECT U0."id"
     FROM "auth_user" U0) LIMIT 21
Run Code Online (Sandbox Code Playgroud)

User.objects.filter(id__in=User.objects.all().values_list("id",flat=True))

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 "auth_user"."id" IN
    (SELECT U0."id"
     FROM "auth_user" U0) LIMIT 21
Run Code Online (Sandbox Code Playgroud)

注意到什么了吗?它们是完全相同的查询。User.objects.none()同样有趣的是,如果您尝试诸如、User.objects.filter(id__in=[])和 之类的事情会发生什么User.objects.filter(id__in=User.objects.none()。在所有这三种情况下,Django 都会短路查询。换句话说,它甚至不会向数据库发出查询,因为它事先确定不会有任何结果。我在这里最好的猜测是,添加values_list到末尾会破坏短路逻辑,允许发送实际的查询,并且它实际上values_list决定了应该发送的查询。当您仔细考虑时,这两种情况实际上是相同的。id无论哪种方式,您都只想在未过滤的查询集上进行选择。

我强调了这一部分,因为我确信您现在会跳上跳下地说但none应该返回一个空查询集。确实如此,但它是通过自动返回EmptyQuerySet并且根本不实际查询数据库来实现这一点的。它不会向查询添加任何过滤器。

这是否是一个错误还有待商榷。我更倾向于将其称为边缘情况,很可能无法真正“修复”。它取决于在这个场景中所有交织的部分如何组合在一起。