Commit bfb11b95 authored by Baptiste Mispelon's avatar Baptiste Mispelon
Browse files

Fixed #23795 -- Fixed a regression in custom form fields

Custom form fields having a `queryset` attribute but no
`limit_choices_to` could no longer be used in ModelForms.

Refs #2445.

Thanks to artscoop for the report.
parent 11b7680d
Loading
Loading
Loading
Loading
+0 −11
Original line number Diff line number Diff line
@@ -181,17 +181,6 @@ class Field(six.with_metaclass(RenameFieldMethods, object)):
        """
        return {}

    def get_limit_choices_to(self):
        """
        Returns ``limit_choices_to`` for this form field.

        If it is a callable, it will be invoked and the result will be
        returned.
        """
        if callable(self.limit_choices_to):
            return self.limit_choices_to()
        return self.limit_choices_to

    def has_changed(self, initial, data):
        """
        Return True if data differs from initial.
+13 −4
Original line number Diff line number Diff line
@@ -328,11 +328,9 @@ class BaseModelForm(BaseForm):
        # Apply ``limit_choices_to`` to each field.
        for field_name in self.fields:
            formfield = self.fields[field_name]
            if hasattr(formfield, 'queryset'):
                limit_choices_to = formfield.limit_choices_to
            if hasattr(formfield, 'queryset') and hasattr(formfield, 'get_limit_choices_to'):
                limit_choices_to = formfield.get_limit_choices_to()
                if limit_choices_to is not None:
                    if callable(limit_choices_to):
                        limit_choices_to = limit_choices_to()
                    formfield.queryset = formfield.queryset.complex_filter(limit_choices_to)

    def _get_validation_exclusions(self):
@@ -1133,6 +1131,17 @@ class ModelChoiceField(ChoiceField):
        self.choice_cache = None
        self.to_field_name = to_field_name

    def get_limit_choices_to(self):
        """
        Returns ``limit_choices_to`` for this form field.

        If it is a callable, it will be invoked and the result will be
        returned.
        """
        if callable(self.limit_choices_to):
            return self.limit_choices_to()
        return self.limit_choices_to

    def __deepcopy__(self, memo):
        result = super(ChoiceField, self).__deepcopy__(memo)
        # Need to force a new ModelChoiceIterator to be created, bug #11183
+4 −0
Original line number Diff line number Diff line
@@ -52,3 +52,7 @@ Bugfixes

* Fixed a migration serializing bug involving ``float("nan")`` and
  ``float("inf")`` (:ticket:23770:).

* Fixed a regression where custom form fields having a ``queryset`` attribute
  but no ``limit_choices_to`` could not be used in a
  :class:`~django.forms.ModelForm` (:ticket:`23795`).
+19 −0
Original line number Diff line number Diff line
@@ -2377,6 +2377,17 @@ class StumpJokeForm(forms.ModelForm):
        fields = '__all__'


class CustomFieldWithQuerysetButNoLimitChoicesTo(forms.Field):
    queryset = 42


class StumpJokeWithCustomFieldForm(forms.ModelForm):
    custom = CustomFieldWithQuerysetButNoLimitChoicesTo()
    class Meta:
        model = StumpJoke
        fields = ()  # We don't need any fields from the model


class LimitChoicesToTest(TestCase):
    """
    Tests the functionality of ``limit_choices_to``.
@@ -2407,6 +2418,14 @@ class LimitChoicesToTest(TestCase):
        self.assertIn(self.threepwood, stumpjokeform.fields['has_fooled_today'].queryset)
        self.assertNotIn(self.marley, stumpjokeform.fields['has_fooled_today'].queryset)

    def test_custom_field_with_queryset_but_no_limit_choices_to(self):
        """
        Regression test for #23795: Make sure a custom field with a `queryset`
        attribute but no `limit_choices_to` still works.
        """
        f = StumpJokeWithCustomFieldForm()
        self.assertEqual(f.fields['custom'].queryset, 42)


class FormFieldCallbackTests(TestCase):