Fixed #25294 -- Allowed custom BoundFields on forms.
This commit is contained in:
parent
8615e41586
commit
535809e121
|
@ -204,6 +204,14 @@ class Field(six.with_metaclass(RenameFieldMethods, object)):
|
||||||
data_value = data if data is not None else ''
|
data_value = data if data is not None else ''
|
||||||
return initial_value != data_value
|
return initial_value != data_value
|
||||||
|
|
||||||
|
def get_bound_field(self, form, field_name):
|
||||||
|
"""
|
||||||
|
Return a BoundField instance that will be used when accessing the form
|
||||||
|
field in a template.
|
||||||
|
"""
|
||||||
|
from django.forms.forms import BoundField
|
||||||
|
return BoundField(form, self, field_name)
|
||||||
|
|
||||||
def __deepcopy__(self, memo):
|
def __deepcopy__(self, memo):
|
||||||
result = copy.copy(self)
|
result = copy.copy(self)
|
||||||
memo[id(self)] = result
|
memo[id(self)] = result
|
||||||
|
|
|
@ -152,7 +152,7 @@ class BaseForm(object):
|
||||||
raise KeyError(
|
raise KeyError(
|
||||||
"Key %r not found in '%s'" % (name, self.__class__.__name__))
|
"Key %r not found in '%s'" % (name, self.__class__.__name__))
|
||||||
if name not in self._bound_fields_cache:
|
if name not in self._bound_fields_cache:
|
||||||
self._bound_fields_cache[name] = BoundField(self, field, name)
|
self._bound_fields_cache[name] = field.get_bound_field(self, name)
|
||||||
return self._bound_fields_cache[name]
|
return self._bound_fields_cache[name]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -932,6 +932,48 @@ and using the template above, would render something like:
|
||||||
|
|
||||||
<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" />
|
<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" />
|
||||||
|
|
||||||
|
Customizing ``BoundField``
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
.. versionadded:: 1.9
|
||||||
|
|
||||||
|
If you need to access some additional information about a form field in a
|
||||||
|
template and using a subclass of :class:`~django.forms.Field` isn't
|
||||||
|
sufficient, consider also customizing :class:`~django.forms.BoundField`.
|
||||||
|
|
||||||
|
A custom form field can override ``get_bound_field()``:
|
||||||
|
|
||||||
|
.. method:: Field.get_bound_field(form, field_name)
|
||||||
|
|
||||||
|
Takes an instance of :class:`~django.forms.Form` and the name of the field.
|
||||||
|
The return value will be used when accessing the field in a template. Most
|
||||||
|
likely it will be an instance of a subclass of
|
||||||
|
:class:`~django.forms.BoundField`.
|
||||||
|
|
||||||
|
If you have a ``GPSCoordinatesField``, for example, and want to be able to
|
||||||
|
access additional information about the coordinates in a template, this could
|
||||||
|
be implemented as follows::
|
||||||
|
|
||||||
|
class GPSCoordinatesBoundField(BoundField):
|
||||||
|
@property
|
||||||
|
def country(self):
|
||||||
|
"""
|
||||||
|
Return the country the coordinates lie in or None if it can't be
|
||||||
|
determined.
|
||||||
|
"""
|
||||||
|
value = self.value()
|
||||||
|
if value:
|
||||||
|
return get_country_from_coordinates(value)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
class GPSCoordinatesField(Field):
|
||||||
|
def get_bound_field(self, form, field_name):
|
||||||
|
return GPSCoordinatesBoundField(form, self, field_name)
|
||||||
|
|
||||||
|
Now you can access the country in a template with
|
||||||
|
``{{ form.coordinates.country }}``.
|
||||||
|
|
||||||
.. _binding-uploaded-files:
|
.. _binding-uploaded-files:
|
||||||
|
|
||||||
Binding uploaded files to a form
|
Binding uploaded files to a form
|
||||||
|
|
|
@ -1239,3 +1239,6 @@ custom ``Field`` classes. To do this, just create a subclass of
|
||||||
``clean()`` method and that its ``__init__()`` method accept the core arguments
|
``clean()`` method and that its ``__init__()`` method accept the core arguments
|
||||||
mentioned above (``required``, ``label``, ``initial``, ``widget``,
|
mentioned above (``required``, ``label``, ``initial``, ``widget``,
|
||||||
``help_text``).
|
``help_text``).
|
||||||
|
|
||||||
|
You can also customize how a field will be accessed by overriding
|
||||||
|
:meth:`~django.forms.Field.get_bound_field()`.
|
||||||
|
|
|
@ -364,6 +364,9 @@ Forms
|
||||||
* Form fields now support the :attr:`~django.forms.Field.disabled` argument,
|
* Form fields now support the :attr:`~django.forms.Field.disabled` argument,
|
||||||
allowing the field widget to be displayed disabled by browsers.
|
allowing the field widget to be displayed disabled by browsers.
|
||||||
|
|
||||||
|
* It's now possible to customize bound fields by overriding a field's
|
||||||
|
:meth:`~django.forms.Field.get_bound_field()` method.
|
||||||
|
|
||||||
Generic Views
|
Generic Views
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -1903,6 +1903,17 @@ Password: <input type="password" name="password" /></li>
|
||||||
f = SampleForm(data={'name': 'bar'})
|
f = SampleForm(data={'name': 'bar'})
|
||||||
self.assertIsInstance(force_text(f['name']), SafeData)
|
self.assertIsInstance(force_text(f['name']), SafeData)
|
||||||
|
|
||||||
|
def test_custom_boundfield(self):
|
||||||
|
class CustomField(CharField):
|
||||||
|
def get_bound_field(self, form, name):
|
||||||
|
return (form, name)
|
||||||
|
|
||||||
|
class SampleForm(Form):
|
||||||
|
name = CustomField()
|
||||||
|
|
||||||
|
f = SampleForm()
|
||||||
|
self.assertEqual(f['name'], (f, 'name'))
|
||||||
|
|
||||||
def test_initial_datetime_values(self):
|
def test_initial_datetime_values(self):
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
# Nix microseconds (since they should be ignored). #22502
|
# Nix microseconds (since they should be ignored). #22502
|
||||||
|
|
Loading…
Reference in New Issue