#20242 closed Bug (fixed)
Django queryset 'in' operator fails on first call
Reported by: | anonymous | Owned by: | svisser |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.5 |
Severity: | Normal | Keywords: | |
Cc: | bmispelon@… | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
When using the 'in' operator on a queryset, the first time the call is made it fails. If 'prefetch_related' is removed and 'all' is used instead then the problem is gone.
from django.db import models class Category(models.Model): name = models.CharField(max_length=100) class Project(models.Model): categories = models.ManyToManyField(Category, related_name='projects') category_list = Category.objects.prefetch_related('projects') print category_list # [<Category: Category object>, <Category: Category object>] print category_list[0] in category_list # False print category_list[0] in category_list # True
Change History (6)
comment:1 by , 12 years ago
Cc: | added |
---|---|
Triage Stage: | Unreviewed → Accepted |
comment:2 by , 11 years ago
Has patch: | set |
---|
The reason it happens is that, when self._result_cache
of a QuerySet is None
, the __contains__
method of a Queryset executes the part it = iter(self)
.
The remaining code in __contains__
then assumes that self._result_cache
is equal to the empty list. But that assumption no longer holds because the prefetching (due to the len(self)
call in __iter__
) causes the result cache to be populated. Hence, self._iter
is None
and self._result_cache
contains values but the remaining logic in __contains__
does not take that into account. This means it'll return False
because self._iter
is indeed None
but there are still unchecked items in self._result_cache
.
This can be fixed by only checking that self._iter
is None
when len(self._result_cache) <= pos
holds. If that doesn't hold, it means that there are still unchecked items in self._result_cache
and we need to check those first before giving up.
Pull request: https://github.com/django/django/pull/1060
In fairness, I have added myself to AUTHORS as well in this commit as it took me a while to familiarize myself with the code.
comment:3 by , 11 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:4 by , 11 years ago
It looks like this has been fixed with the removal of chunked reads from QuerySet iteration (the __contains__
method no longer exists) [70679243d1786e03557c28929f9762a119e3ac14]. I'll commit the regression test though.
comment:5 by , 11 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Thanks for the detailed report.
I can reproduce the issue and I'll add that as you implied, it seems to be related to
prefetch_related
since the problem does not appear when you useall()
instead ofprefetch_related('projects')
.