Commit de570d4d authored by Tim Graham's avatar Tim Graham
Browse files

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

Backport of 6afa6818 from master
parent bf428888
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -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):
+2 −1
Original line number Diff line number Diff line
@@ -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`).
+23 −0
Original line number Diff line number Diff line
@@ -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')