[1.7.x] 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.

Backport of bfb11b9562 from master.

Conflicts:
	django/forms/fields.py
This commit is contained in:
Baptiste Mispelon 2014-11-12 21:18:11 +01:00
parent 3e0d7de8a6
commit 606c57a132
4 changed files with 36 additions and 15 deletions

View File

@ -171,17 +171,6 @@ class Field(object):
""" """
return {} 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): def _has_changed(self, initial, data):
""" """
Return True if data differs from initial. Return True if data differs from initial.

View File

@ -329,11 +329,9 @@ class BaseModelForm(BaseForm):
# Apply ``limit_choices_to`` to each field. # Apply ``limit_choices_to`` to each field.
for field_name in self.fields: for field_name in self.fields:
formfield = self.fields[field_name] formfield = self.fields[field_name]
if hasattr(formfield, 'queryset'): if hasattr(formfield, 'queryset') and hasattr(formfield, 'get_limit_choices_to'):
limit_choices_to = formfield.limit_choices_to limit_choices_to = formfield.get_limit_choices_to()
if limit_choices_to is not None: 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) formfield.queryset = formfield.queryset.complex_filter(limit_choices_to)
def _get_validation_exclusions(self): def _get_validation_exclusions(self):
@ -1118,6 +1116,17 @@ class ModelChoiceField(ChoiceField):
self.choice_cache = None self.choice_cache = None
self.to_field_name = to_field_name 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): def __deepcopy__(self, memo):
result = super(ChoiceField, self).__deepcopy__(memo) result = super(ChoiceField, self).__deepcopy__(memo)
# Need to force a new ModelChoiceIterator to be created, bug #11183 # Need to force a new ModelChoiceIterator to be created, bug #11183

View File

@ -52,3 +52,7 @@ Bugfixes
* Fixed a migration serializing bug involving ``float("nan")`` and * Fixed a migration serializing bug involving ``float("nan")`` and
``float("inf")`` (:ticket:23770:). ``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`).

View File

@ -2345,6 +2345,17 @@ class StumpJokeForm(forms.ModelForm):
fields = '__all__' 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): class LimitChoicesToTest(TestCase):
""" """
Tests the functionality of ``limit_choices_to``. Tests the functionality of ``limit_choices_to``.
@ -2375,6 +2386,14 @@ class LimitChoicesToTest(TestCase):
self.assertIn(self.threepwood, stumpjokeform.fields['has_fooled_today'].queryset) self.assertIn(self.threepwood, stumpjokeform.fields['has_fooled_today'].queryset)
self.assertNotIn(self.marley, 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): class FormFieldCallbackTests(TestCase):