Recently found something peculiar in a filter, I can't believe its intended behaviour.
最近在过滤器中发现了一些特殊的东西,我无法相信它的预期行为。
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))
Oddly both of these lists return the full set of users. It actually seems to be pretty easy to "fix" if I wrap the inner query in a list function e.g.
奇怪的是,这两个列表都返回了完整的用户集。如果我将内部查询包装在列表函数中,实际上似乎很容易“修复”,例如
User.objects.filter(id__in=list(User.objects.none().values_list("id")))
Then this returns what I would expect (an empty list).
然后这会返回我期望的(空列表)。
Seems like a bug to me, or am I missing something?
对我来说似乎是一个错误,或者我错过了什么?
Steve
1 个解决方案
#1
1
Here's the queries produced for both:
以下是为两者生成的查询:
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
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
Notice anything? They're exactly the same queries. Also interesting is what happens if you try things like User.objects.none()
, User.objects.filter(id__in=[])
and User.objects.filter(id__in=User.objects.none()
. In all three of these circumstances, Django short-circuits the query. In other words, it doesn't even issue a query to the database because it determines beforehand that there will not be any results. My best guess here is that adding values_list
to the end defeats the short-circuiting logic, allowing an actual query to be send, and that it's actually values_list
that determines the query that should be sent. Which in both cases is really the same, when you think about it. Either way you want to select just id
on an unfiltered queryset.
注意什么?他们是完全相同的查询。同样有趣的是,如果您尝试User.objects.none(),User.objects.filter(id__in = [])和User.objects.filter(id__in = User.objects.none()等内容会发生什么。在这些情况下,Django会使查询短路。换句话说,它甚至不会向数据库发出查询,因为它事先确定不会有任何结果。我最好的猜测是在最后添加values_list会导致失败。短路逻辑,允许发送实际查询,并且它实际上是values_list,用于确定应该发送的查询。当你想到它时,两种情况都是相同的。无论哪种方式,你都想选择id在未过滤的查询集上。
I emphasized that part, because I'm sure you're jumping up and down now saying but none
should return an empty queryset. True, but it does so by virtue of automatically returning an EmptyQuerySet
and never actually querying the database at all. It doesn't add any filters to the query.
我强调了那一部分,因为我确定你现在正在上下跳,但是没有人应该返回一个空的查询集。是的,但它是通过自动返回一个EmptyQuerySet并且根本不会实际查询数据库来实现的。它不会向查询添加任何过滤器。
Whether this is a bug or not is debatable. I'm more apt to call this an edge-case that most likely can't really be "fixed". It's a function of how all the interweaving parts come together in this one scenario.
这是否是一个错误是值得商榷的。我更倾向于把它称为边缘情况,很可能不会真正“修复”。它是在这一场景中所有交织部分如何组合在一起的功能。
#1
1
Here's the queries produced for both:
以下是为两者生成的查询:
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
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
Notice anything? They're exactly the same queries. Also interesting is what happens if you try things like User.objects.none()
, User.objects.filter(id__in=[])
and User.objects.filter(id__in=User.objects.none()
. In all three of these circumstances, Django short-circuits the query. In other words, it doesn't even issue a query to the database because it determines beforehand that there will not be any results. My best guess here is that adding values_list
to the end defeats the short-circuiting logic, allowing an actual query to be send, and that it's actually values_list
that determines the query that should be sent. Which in both cases is really the same, when you think about it. Either way you want to select just id
on an unfiltered queryset.
注意什么?他们是完全相同的查询。同样有趣的是,如果您尝试User.objects.none(),User.objects.filter(id__in = [])和User.objects.filter(id__in = User.objects.none()等内容会发生什么。在这些情况下,Django会使查询短路。换句话说,它甚至不会向数据库发出查询,因为它事先确定不会有任何结果。我最好的猜测是在最后添加values_list会导致失败。短路逻辑,允许发送实际查询,并且它实际上是values_list,用于确定应该发送的查询。当你想到它时,两种情况都是相同的。无论哪种方式,你都想选择id在未过滤的查询集上。
I emphasized that part, because I'm sure you're jumping up and down now saying but none
should return an empty queryset. True, but it does so by virtue of automatically returning an EmptyQuerySet
and never actually querying the database at all. It doesn't add any filters to the query.
我强调了那一部分,因为我确定你现在正在上下跳,但是没有人应该返回一个空的查询集。是的,但它是通过自动返回一个EmptyQuerySet并且根本不会实际查询数据库来实现的。它不会向查询添加任何过滤器。
Whether this is a bug or not is debatable. I'm more apt to call this an edge-case that most likely can't really be "fixed". It's a function of how all the interweaving parts come together in this one scenario.
这是否是一个错误是值得商榷的。我更倾向于把它称为边缘情况,很可能不会真正“修复”。它是在这一场景中所有交织部分如何组合在一起的功能。