summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Meyer <carl@oddbird.net>2013-08-29 21:44:37 -0600
committerCarl Meyer <carl@oddbird.net>2013-08-30 17:45:14 -0600
commit21a3efcf48559b3336ca1743d94c741a82feffd6 (patch)
tree6d3140dab63f2f8081d8da4d578e91493dd196c7
parent1d874ce0f94432c1a4b42cacaeb02c5948000d0f (diff)
[1.6.x] Fixed #20999 - Allow overriding formfield class with choices, without subclass restrictions.
Refs #18162. Thanks claudep and mjtamlyn for review. Backport of 7211741fc5d50425 from master.
-rw-r--r--django/db/models/fields/__init__.py6
-rw-r--r--docs/howto/custom-model-fields.txt20
-rw-r--r--tests/model_fields/tests.py12
3 files changed, 22 insertions, 16 deletions
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index e2f8c6c48e..0621ef912e 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -515,7 +515,7 @@ class Field(object):
def save_form_data(self, instance, data):
setattr(instance, self.name, data)
- def formfield(self, form_class=None, **kwargs):
+ def formfield(self, form_class=None, choices_form_class=None, **kwargs):
"""
Returns a django.forms.Field instance for this database Field.
"""
@@ -536,7 +536,9 @@ class Field(object):
defaults['coerce'] = self.to_python
if self.null:
defaults['empty_value'] = None
- if form_class is None or not issubclass(form_class, forms.TypedChoiceField):
+ if choices_form_class is not None:
+ form_class = choices_form_class
+ else:
form_class = forms.TypedChoiceField
# Many of the subclass-specific formfield arguments (min_value,
# max_value) don't apply for choice fields, so be sure to only pass
diff --git a/docs/howto/custom-model-fields.txt b/docs/howto/custom-model-fields.txt
index af652de78c..7abecefb8b 100644
--- a/docs/howto/custom-model-fields.txt
+++ b/docs/howto/custom-model-fields.txt
@@ -617,17 +617,23 @@ prepared with :meth:`.get_prep_lookup`.
Specifying the form field for a model field
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. method:: Field.formfield(self, form_class=forms.CharField, **kwargs)
+.. method:: Field.formfield(self, form_class=None, choices_form_class=None, **kwargs)
-Returns the default form field to use when this field is displayed in a model.
-This method is called by the :class:`~django.forms.ModelForm` helper.
+Returns the default form field to use when this model field is displayed in a
+form. This method is called by the :class:`~django.forms.ModelForm` helper.
+
+The form field class can be specified via the ``form_class`` and
+``choices_form_class`` arguments; the latter is used if the field has choices
+specified, the former otherwise. If these arguments are not provided,
+:class:`~django.forms.CharField` or :class:`~django.forms.TypedChoiceField`
+will be used.
All of the ``kwargs`` dictionary is passed directly to the form field's
``__init__()`` method. Normally, all you need to do is set up a good default
-for the ``form_class`` argument and then delegate further handling to the
-parent class. This might require you to write a custom form field (and even a
-form widget). See the :doc:`forms documentation </topics/forms/index>` for
-information about this.
+for the ``form_class`` (and maybe ``choices_form_class``) argument and then
+delegate further handling to the parent class. This might require you to write
+a custom form field (and even a form widget). See the :doc:`forms documentation
+</topics/forms/index>` for information about this.
Continuing our ongoing example, we can write the :meth:`.formfield` method as::
diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py
index 432984f6bb..60ce13dfb6 100644
--- a/tests/model_fields/tests.py
+++ b/tests/model_fields/tests.py
@@ -68,14 +68,12 @@ class BasicFieldTests(test.TestCase):
self.assertEqual(m._meta.get_field('id').verbose_name, 'verbose pk')
- def test_formclass_with_choices(self):
- # regression for 18162
- class CustomChoiceField(forms.TypedChoiceField):
- pass
- choices = [('a@b.cc', 'a@b.cc'), ('b@b.cc', 'b@b.cc')]
+ def test_choices_form_class(self):
+ """Can supply a custom choices form class. Regression for #20999."""
+ choices = [('a', 'a')]
field = models.CharField(choices=choices)
- klass = CustomChoiceField
- self.assertIsInstance(field.formfield(form_class=klass), klass)
+ klass = forms.TypedMultipleChoiceField
+ self.assertIsInstance(field.formfield(choices_form_class=klass), klass)
class DecimalFieldTests(test.TestCase):