[1.8.x] Fixed #25496 -- Made ModelChoiceField respect prefetch_related().

Backport of 6afa6818fc from master
This commit is contained in:
Tim Graham 2015-10-05 19:25:21 -04:00
parent bf42888801
commit de570d4da9
3 changed files with 29 additions and 3 deletions

View File

@ -1092,15 +1092,17 @@ class ModelChoiceIterator(object):
def __iter__(self):
if self.field.empty_label is not None:
yield ("", self.field.empty_label)
method = 'all' if self.queryset._prefetch_related_lookups else 'iterator'
queryset = getattr(self.queryset, method)
if self.field.cache_choices:
if self.field.choice_cache is None:
self.field.choice_cache = [
self.choice(obj) for obj in self.queryset.iterator()
self.choice(obj) for obj in queryset()
]
for choice in self.field.choice_cache:
yield choice
else:
for obj in self.queryset.iterator():
for obj in queryset():
yield self.choice(obj)
def __len__(self):

View File

@ -9,4 +9,5 @@ Django 1.8.6 fixes several bugs in 1.8.5.
Bugfixes
========
* ...
* Fixed a regression causing ``ModelChoiceField`` to ignore
``prefetch_related()`` on its queryset (:ticket:`25496`).

View File

@ -2203,6 +2203,29 @@ class OtherModelFormTests(TestCase):
with self.assertRaises(ValidationError):
f.fields['status'].clean('z')
def test_prefetch_related_queryset(self):
"""
ModelChoiceField should respect a prefetch_related() on its queryset.
"""
blue = Colour.objects.create(name='blue')
red = Colour.objects.create(name='red')
multicolor_item = ColourfulItem.objects.create()
multicolor_item.colours.add(blue, red)
red_item = ColourfulItem.objects.create()
red_item.colours.add(red)
class ColorModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return ', '.join(c.name for c in obj.colours.all())
field = ColorModelChoiceField(ColourfulItem.objects.prefetch_related('colours'))
with self.assertNumQueries(4): # would be 5 if prefetch is ignored
self.assertEqual(tuple(field.choices), (
('', '---------'),
(multicolor_item.pk, 'blue, red'),
(red_item.pk, 'red'),
))
def test_foreignkeys_which_use_to_field(self):
apple = Inventory.objects.create(barcode=86, name='Apple')
Inventory.objects.create(barcode=22, name='Pear')