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
Django provides a filter() method which returns a subset of data. It accepts field names as keyword arguments and returns a QuerySet object. As database has only one record where name is 'tom' , the QuerySet object contains only a single record.
Working with Filter Easily the most important method when working with Django models and the underlying QuerySets is the filter() method, which allows you to generate a QuerySet of objects that match a particular set of filtered parameters.
A QuerySet is a collection of data from a database. A QuerySet is built up as a list of objects. QuerySets makes it easier to get the data you actually need, by allowing you to filter and order the data. In this tutorial we will be querying data from the Members table.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With