mirror of https://github.com/django/django.git
Fixed #25078 -- Added support for disabled form fields
Thanks Keryn Knight and Tim Graham for the reviews.
This commit is contained in:
parent
1fed8dd715
commit
1ef4aeab40
|
@ -70,7 +70,7 @@ class Field(six.with_metaclass(RenameFieldMethods, object)):
|
||||||
|
|
||||||
def __init__(self, required=True, widget=None, label=None, initial=None,
|
def __init__(self, required=True, widget=None, label=None, initial=None,
|
||||||
help_text='', error_messages=None, show_hidden_initial=False,
|
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.
|
# required -- Boolean that specifies whether the field is required.
|
||||||
# True by default.
|
# True by default.
|
||||||
# widget -- A Widget class, or instance of a Widget class, that should
|
# 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.
|
# hidden widget with initial value after widget.
|
||||||
# validators -- List of additional validators to use
|
# validators -- List of additional validators to use
|
||||||
# localize -- Boolean that specifies if the field should be localized.
|
# 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
|
# label_suffix -- Suffix to be added to the label. Overrides
|
||||||
# form's label_suffix.
|
# form's label_suffix.
|
||||||
self.required, self.label, self.initial = required, label, initial
|
self.required, self.label, self.initial = required, label, initial
|
||||||
self.show_hidden_initial = show_hidden_initial
|
self.show_hidden_initial = show_hidden_initial
|
||||||
self.help_text = help_text
|
self.help_text = help_text
|
||||||
|
self.disabled = disabled
|
||||||
self.label_suffix = label_suffix
|
self.label_suffix = label_suffix
|
||||||
widget = widget or self.widget
|
widget = widget or self.widget
|
||||||
if isinstance(widget, type):
|
if isinstance(widget, type):
|
||||||
|
|
|
@ -386,7 +386,10 @@ class BaseForm(object):
|
||||||
# value_from_datadict() gets the data from the data dictionaries.
|
# value_from_datadict() gets the data from the data dictionaries.
|
||||||
# Each widget type knows how to retrieve its own data, because some
|
# Each widget type knows how to retrieve its own data, because some
|
||||||
# widgets split data over several HTML fields.
|
# 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:
|
try:
|
||||||
if isinstance(field, FileField):
|
if isinstance(field, FileField):
|
||||||
initial = self.initial.get(name, field.initial)
|
initial = self.initial.get(name, field.initial)
|
||||||
|
@ -567,6 +570,8 @@ class BoundField(object):
|
||||||
widget.is_localized = True
|
widget.is_localized = True
|
||||||
|
|
||||||
attrs = attrs or {}
|
attrs = attrs or {}
|
||||||
|
if self.field.disabled:
|
||||||
|
attrs['disabled'] = True
|
||||||
auto_id = self.auto_id
|
auto_id = self.auto_id
|
||||||
if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
|
if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
|
||||||
if not only_initial:
|
if not only_initial:
|
||||||
|
|
|
@ -299,6 +299,17 @@ as the rendered output.
|
||||||
See the :ref:`format localization <format-localization>` documentation for
|
See the :ref:`format localization <format-localization>` documentation for
|
||||||
more information.
|
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
|
Checking if the field data has changed
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
|
@ -313,6 +313,9 @@ Forms
|
||||||
and trailing whitespace. As this defaults to ``True`` this is different
|
and trailing whitespace. As this defaults to ``True`` this is different
|
||||||
behavior from previous releases.
|
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
|
Generic Views
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -176,6 +176,10 @@ class FieldsTests(SimpleTestCase):
|
||||||
self.assertEqual(f.clean(' 1'), ' 1')
|
self.assertEqual(f.clean(' 1'), ' 1')
|
||||||
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 ################################################################
|
# IntegerField ################################################################
|
||||||
|
|
||||||
def test_integerfield_1(self):
|
def test_integerfield_1(self):
|
||||||
|
@ -1076,6 +1080,12 @@ class FieldsTests(SimpleTestCase):
|
||||||
form = ChoiceFieldForm()
|
form = ChoiceFieldForm()
|
||||||
self.assertEqual([('P', 'Paul')], list(form.fields['choicefield'].choices))
|
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 ############################################################
|
||||||
# TypedChoiceField is just like ChoiceField, except that coerced types will
|
# TypedChoiceField is just like ChoiceField, except that coerced types will
|
||||||
# be returned:
|
# be returned:
|
||||||
|
|
|
@ -501,6 +501,37 @@ class FormsTestCase(SimpleTestCase):
|
||||||
<option value="P" selected="selected">Paul McCartney</option>
|
<option value="P" selected="selected">Paul McCartney</option>
|
||||||
</select>""")
|
</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):
|
def test_hidden_data(self):
|
||||||
class SongForm(Form):
|
class SongForm(Form):
|
||||||
name = CharField()
|
name = CharField()
|
||||||
|
|
Loading…
Reference in New Issue