Fixed #29158 -- Fixed len(choices) crash if ModelChoiceField's queryset is a manager.

Removing all() in __iter__() prevents a duplicate query when choices are
cast to a list and there's a prefetch_related().
This commit is contained in:
François Freitag 2018-02-23 21:12:09 -08:00 committed by Tim Graham
parent 06172d7bc2
commit 40f0aa9885
3 changed files with 4 additions and 3 deletions

View File

@ -1130,7 +1130,7 @@ class ModelChoiceIterator:
def __iter__(self): def __iter__(self):
if self.field.empty_label is not None: if self.field.empty_label is not None:
yield ("", self.field.empty_label) yield ("", self.field.empty_label)
queryset = self.queryset.all() queryset = self.queryset
# Can't use iterator() when queryset uses prefetch_related() # Can't use iterator() when queryset uses prefetch_related()
if not queryset._prefetch_related_lookups: if not queryset._prefetch_related_lookups:
queryset = queryset.iterator() queryset = queryset.iterator()
@ -1194,7 +1194,7 @@ class ModelChoiceField(ChoiceField):
return self._queryset return self._queryset
def _set_queryset(self, queryset): def _set_queryset(self, queryset):
self._queryset = queryset self._queryset = None if queryset is None else queryset.all()
self.widget.choices = self.choices self.widget.choices = self.choices
queryset = property(_get_queryset, _set_queryset) queryset = property(_get_queryset, _set_queryset)

View File

@ -249,6 +249,7 @@ class ModelChoiceFieldTests(TestCase):
def test_queryset_manager(self): def test_queryset_manager(self):
f = forms.ModelChoiceField(Category.objects) f = forms.ModelChoiceField(Category.objects)
self.assertEqual(len(f.choices), 4)
self.assertEqual(list(f.choices), [ self.assertEqual(list(f.choices), [
('', '---------'), ('', '---------'),
(self.c1.pk, 'Entertainment'), (self.c1.pk, 'Entertainment'),

View File

@ -2339,7 +2339,7 @@ class OtherModelFormTests(TestCase):
return ', '.join(c.name for c in obj.colours.all()) return ', '.join(c.name for c in obj.colours.all())
field = ColorModelChoiceField(ColourfulItem.objects.prefetch_related('colours')) field = ColorModelChoiceField(ColourfulItem.objects.prefetch_related('colours'))
with self.assertNumQueries(4): # would be 5 if prefetch is ignored with self.assertNumQueries(2): # would be 3 if prefetch is ignored
self.assertEqual(tuple(field.choices), ( self.assertEqual(tuple(field.choices), (
('', '---------'), ('', '---------'),
(multicolor_item.pk, 'blue, red'), (multicolor_item.pk, 'blue, red'),