summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2015-07-07 21:06:57 +0200
committerClaude Paroz <claude@2xlibre.net>2015-07-16 19:36:56 +0200
commit1ef4aeab403f7fb74c0b9b11fde854fd03afc40c (patch)
treeb3852ad0aefffcf4fd29aacc19e1ca9529d9cdb9
parent1fed8dd715cc734f51c162d4362d34d357925bc4 (diff)
Fixed #25078 -- Added support for disabled form fields
Thanks Keryn Knight and Tim Graham for the reviews.
-rw-r--r--django/forms/fields.py5
-rw-r--r--django/forms/forms.py7
-rw-r--r--docs/ref/forms/fields.txt11
-rw-r--r--docs/releases/1.9.txt3
-rw-r--r--tests/forms_tests/tests/test_fields.py10
-rw-r--r--tests/forms_tests/tests/test_forms.py31
6 files changed, 65 insertions, 2 deletions
diff --git a/django/forms/fields.py b/django/forms/fields.py
index afbefb64c0..128b6bcbf1 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -70,7 +70,7 @@ class Field(six.with_metaclass(RenameFieldMethods, object)):
def __init__(self, required=True, widget=None, label=None, initial=None,
help_text='', error_messages=None, show_hidden_initial=False,
- validators=[], localize=False, label_suffix=None):
+ validators=[], localize=False, disabled=False, label_suffix=None):
# required -- Boolean that specifies whether the field is required.
# True by default.
# widget -- A Widget class, or instance of a Widget class, that should
@@ -90,11 +90,14 @@ class Field(six.with_metaclass(RenameFieldMethods, object)):
# hidden widget with initial value after widget.
# validators -- List of additional validators to use
# localize -- Boolean that specifies if the field should be localized.
+ # disabled -- Boolean that specifies whether the field is disabled, that
+ # is its widget is shown in the form but not editable.
# label_suffix -- Suffix to be added to the label. Overrides
# form's label_suffix.
self.required, self.label, self.initial = required, label, initial
self.show_hidden_initial = show_hidden_initial
self.help_text = help_text
+ self.disabled = disabled
self.label_suffix = label_suffix
widget = widget or self.widget
if isinstance(widget, type):
diff --git a/django/forms/forms.py b/django/forms/forms.py
index 4ee79d13ce..ad9e87cae4 100644
--- a/django/forms/forms.py
+++ b/django/forms/forms.py
@@ -386,7 +386,10 @@ class BaseForm(object):
# value_from_datadict() gets the data from the data dictionaries.
# Each widget type knows how to retrieve its own data, because some
# widgets split data over several HTML fields.
- value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
+ if field.disabled:
+ value = self.initial.get(name, field.initial)
+ else:
+ value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, FileField):
initial = self.initial.get(name, field.initial)
@@ -567,6 +570,8 @@ class BoundField(object):
widget.is_localized = True
attrs = attrs or {}
+ if self.field.disabled:
+ attrs['disabled'] = True
auto_id = self.auto_id
if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
if not only_initial:
diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt
index c4a40021d0..776ef873ad 100644
--- a/docs/ref/forms/fields.txt
+++ b/docs/ref/forms/fields.txt
@@ -299,6 +299,17 @@ as the rendered output.
See the :ref:`format localization <format-localization>` documentation for
more information.
+``disabled``
+~~~~~~~~~~~~
+
+.. attribute:: Field.disabled
+
+.. versionadded:: 1.9
+
+The ``disabled`` boolean argument, when set to ``True``, disables a form field
+using the ``disabled`` HTML attribute so that it won't be editable by users.
+Even if a user tampers with the field's value submitted to the server, it will
+be ignored in favor of the value from the form's initial data.
Checking if the field data has changed
--------------------------------------
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
index 783009db07..6dc0e8473f 100644
--- a/docs/releases/1.9.txt
+++ b/docs/releases/1.9.txt
@@ -313,6 +313,9 @@ Forms
and trailing whitespace. As this defaults to ``True`` this is different
behavior from previous releases.
+* Form fields now support the :attr:`~django.forms.Field.disabled` argument,
+ allowing the field widget to be displayed disabled by browsers.
+
Generic Views
^^^^^^^^^^^^^
diff --git a/tests/forms_tests/tests/test_fields.py b/tests/forms_tests/tests/test_fields.py
index 9c341ca7a3..1644bda4e2 100644
--- a/tests/forms_tests/tests/test_fields.py
+++ b/tests/forms_tests/tests/test_fields.py
@@ -176,6 +176,10 @@ class FieldsTests(SimpleTestCase):
self.assertEqual(f.clean(' 1'), ' 1')
self.assertEqual(f.clean('1 '), '1 ')
+ def test_charfield_disabled(self):
+ f = CharField(disabled=True)
+ self.assertWidgetRendersTo(f, '<input type="text" name="f" id="id_f" disabled />')
+
# IntegerField ################################################################
def test_integerfield_1(self):
@@ -1076,6 +1080,12 @@ class FieldsTests(SimpleTestCase):
form = ChoiceFieldForm()
self.assertEqual([('P', 'Paul')], list(form.fields['choicefield'].choices))
+ def test_choicefield_disabled(self):
+ f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')], disabled=True)
+ self.assertWidgetRendersTo(f,
+ '<select id="id_f" name="f" disabled><option value="J">John</option>'
+ '<option value="P">Paul</option></select>')
+
# TypedChoiceField ############################################################
# TypedChoiceField is just like ChoiceField, except that coerced types will
# be returned:
diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py
index 07de828334..acd1b6840a 100644
--- a/tests/forms_tests/tests/test_forms.py
+++ b/tests/forms_tests/tests/test_forms.py
@@ -501,6 +501,37 @@ class FormsTestCase(SimpleTestCase):
<option value="P" selected="selected">Paul McCartney</option>
</select>""")
+ def test_form_with_disabled_fields(self):
+ class PersonForm(Form):
+ name = CharField()
+ birthday = DateField(disabled=True)
+
+ class PersonFormFieldInitial(Form):
+ name = CharField()
+ birthday = DateField(disabled=True, initial=datetime.date(1974, 8, 16))
+
+ # Disabled fields are generally not transmitted by user agents.
+ # The value from the form's initial data is used.
+ f1 = PersonForm({'name': 'John Doe'}, initial={'birthday': datetime.date(1974, 8, 16)})
+ f2 = PersonFormFieldInitial({'name': 'John Doe'})
+ for form in (f1, f2):
+ self.assertTrue(form.is_valid())
+ self.assertEqual(
+ form.cleaned_data,
+ {'birthday': datetime.date(1974, 8, 16), 'name': 'John Doe'}
+ )
+
+ # Values provided in the form's data are ignored.
+ data = {'name': 'John Doe', 'birthday': '1984-11-10'}
+ f1 = PersonForm(data, initial={'birthday': datetime.date(1974, 8, 16)})
+ f2 = PersonFormFieldInitial(data)
+ for form in (f1, f2):
+ self.assertTrue(form.is_valid())
+ self.assertEqual(
+ form.cleaned_data,
+ {'birthday': datetime.date(1974, 8, 16), 'name': 'John Doe'}
+ )
+
def test_hidden_data(self):
class SongForm(Form):
name = CharField()