mirror of
https://github.com/django/django.git
synced 2025-08-04 19:08:28 +00:00
Fixed #35521 -- Allowed overriding BoundField class on fields, forms and renderers.
Thank you Sarah Boyce, Carlton Gibson, Tim Schilling and Adam Johnson for reviews. Co-authored-by: Christophe Henry <contact@c-henry.fr> Co-authored-by: David Smith <smithdc@gmail.com> Co-authored-by: Natalia <124304+nessita@users.noreply.github.com> Co-authored-by: Matthias Kestenholz <mk@feinheit.ch>
This commit is contained in:
parent
0cabed9efa
commit
6a7ee02f59
8 changed files with 362 additions and 14 deletions
|
@ -822,6 +822,9 @@ classes, as needed. The HTML will look something like:
|
|||
>>> f["subject"].legend_tag(attrs={"class": "foo"})
|
||||
<legend for="id_subject" class="foo required">Subject:</legend>
|
||||
|
||||
You may further modify the rendering of form rows by using a
|
||||
:ref:`custom BoundField <custom-boundfield>`.
|
||||
|
||||
.. _ref-forms-api-configuring-label:
|
||||
|
||||
Configuring form elements' HTML ``id`` attributes and ``<label>`` tags
|
||||
|
@ -1149,6 +1152,12 @@ they're not the only way a form object can be displayed.
|
|||
|
||||
The ``__str__()`` method of this object displays the HTML for this field.
|
||||
|
||||
You can use :attr:`.Form.bound_field_class` and
|
||||
:attr:`.Field.bound_field_class` to specify a different ``BoundField`` class
|
||||
per form or per field, respectively.
|
||||
|
||||
See :ref:`custom-boundfield` for examples of overriding a ``BoundField``.
|
||||
|
||||
To retrieve a single ``BoundField``, use dictionary lookup syntax on your form
|
||||
using the field's name as the key:
|
||||
|
||||
|
@ -1488,23 +1497,34 @@ Methods of ``BoundField``
|
|||
>>> print(bound_form["subject"].value())
|
||||
hi
|
||||
|
||||
.. _custom-boundfield:
|
||||
|
||||
Customizing ``BoundField``
|
||||
==========================
|
||||
|
||||
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`.
|
||||
.. attribute:: Form.bound_field_class
|
||||
|
||||
A custom form field can override ``get_bound_field()``:
|
||||
.. versionadded:: 5.2
|
||||
|
||||
.. method:: Field.get_bound_field(form, field_name)
|
||||
Define a custom :class:`~django.forms.BoundField` class to use when rendering
|
||||
the form. This takes precedence over the project-level
|
||||
:attr:`.BaseRenderer.bound_field_class` (along with a custom
|
||||
:setting:`FORM_RENDERER`), but can be overridden by the field-level
|
||||
:attr:`.Field.bound_field_class`.
|
||||
|
||||
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 not defined as a class variable, ``bound_field_class`` can be set via the
|
||||
``bound_field_class`` argument in the :class:`Form` or :class:`Field`
|
||||
constructor.
|
||||
|
||||
If you have a ``GPSCoordinatesField``, for example, and want to be able to
|
||||
For compatibility reasons, a custom form field can still override
|
||||
:meth:`.Field.get_bound_field()` to use a custom class, though any of the
|
||||
previous options are preferred.
|
||||
|
||||
You may want to use a custom :class:`.BoundField` 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.
|
||||
|
||||
For example, if you have a ``GPSCoordinatesField``, and want to be able to
|
||||
access additional information about the coordinates in a template, this could
|
||||
be implemented as follows::
|
||||
|
||||
|
@ -1523,12 +1543,74 @@ be implemented as follows::
|
|||
|
||||
|
||||
class GPSCoordinatesField(Field):
|
||||
def get_bound_field(self, form, field_name):
|
||||
return GPSCoordinatesBoundField(form, self, field_name)
|
||||
bound_field_class = GPSCoordinatesBoundField
|
||||
|
||||
Now you can access the country in a template with
|
||||
``{{ form.coordinates.country }}``.
|
||||
|
||||
You may also want to customize the default form field template rendering. For
|
||||
example, you can override :meth:`.BoundField.label_tag` to add a custom class::
|
||||
|
||||
class StyledLabelBoundField(BoundField):
|
||||
def label_tag(self, contents=None, attrs=None, label_suffix=None, tag=None):
|
||||
attrs = attrs or {}
|
||||
attrs["class"] = "wide"
|
||||
return super().label_tag(contents, attrs, label_suffix, tag)
|
||||
|
||||
|
||||
class UserForm(forms.Form):
|
||||
bound_field_class = StyledLabelBoundField
|
||||
name = CharField()
|
||||
|
||||
This would update the default form rendering:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> f = UserForm()
|
||||
>>> print(f["name"].label_tag)
|
||||
<label for="id_name" class="wide">Name:</label>
|
||||
|
||||
To add a CSS class to the wrapping HTML element of all fields, a ``BoundField``
|
||||
can be overridden to return a different collection of CSS classes::
|
||||
|
||||
class WrappedBoundField(BoundField):
|
||||
def css_classes(self, extra_classes=None):
|
||||
parent_css_classes = super().css_classes(extra_classes)
|
||||
return f"field-class {parent_css_classes}".strip()
|
||||
|
||||
|
||||
class UserForm(forms.Form):
|
||||
bound_field_class = WrappedBoundField
|
||||
name = CharField()
|
||||
|
||||
This would update the form rendering as follows:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> f = UserForm()
|
||||
>>> print(f)
|
||||
<div class="field-class"><label for="id_name">Name:</label><input type="text" name="name" required id="id_name"></div>
|
||||
|
||||
Alternatively, to override the ``BoundField`` class at the project level,
|
||||
:attr:`.BaseRenderer.bound_field_class` can be defined on a custom
|
||||
:setting:`FORM_RENDERER`:
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``mysite/renderers.py``
|
||||
|
||||
from django.forms.renderers import DjangoTemplates
|
||||
|
||||
from .forms import CustomBoundField
|
||||
|
||||
|
||||
class CustomRenderer(DjangoTemplates):
|
||||
bound_field_class = CustomBoundField
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``settings.py``
|
||||
|
||||
FORM_RENDERER = "mysite.renderers.CustomRenderer"
|
||||
|
||||
.. _binding-uploaded-files:
|
||||
|
||||
Binding uploaded files to a form
|
||||
|
|
|
@ -397,6 +397,16 @@ default this value is set to ``"django/forms/field.html"``. Can be changed per
|
|||
field by overriding this attribute or more generally by overriding the default
|
||||
template, see also :ref:`overriding-built-in-field-templates`.
|
||||
|
||||
``bound_field_class``
|
||||
---------------------
|
||||
|
||||
.. attribute:: Field.bound_field_class
|
||||
|
||||
.. versionadded:: 5.2
|
||||
|
||||
The ``bound_field_class`` attribute allows a per-field override of
|
||||
:attr:`.Form.bound_field_class`.
|
||||
|
||||
Checking if the field data has changed
|
||||
======================================
|
||||
|
||||
|
@ -1635,4 +1645,14 @@ only requirements are that it implement a ``clean()`` method and that its
|
|||
``label``, ``initial``, ``widget``, ``help_text``).
|
||||
|
||||
You can also customize how a field will be accessed by overriding
|
||||
:meth:`~django.forms.Field.get_bound_field()`.
|
||||
:attr:`~django.forms.Field.bound_field_class` or override
|
||||
:meth:`.Field.get_bound_field()` if you need more flexibility when creating
|
||||
the ``BoundField``:
|
||||
|
||||
.. method:: Field.get_bound_field(form, field_name)
|
||||
|
||||
Takes an instance of :class:`~django.forms.Form` and the name of the field.
|
||||
The returned :class:`.BoundField` instance will be used when accessing the
|
||||
field in a template.
|
||||
|
||||
See :ref:`custom-boundfield` for examples of overriding a ``BoundField``.
|
||||
|
|
|
@ -65,6 +65,18 @@ should return a rendered templates (as a string) or raise
|
|||
|
||||
Defaults to ``"django/forms/field.html"``
|
||||
|
||||
.. attribute:: bound_field_class
|
||||
|
||||
.. versionadded:: 5.2
|
||||
|
||||
The default class used to represent form fields across the project.
|
||||
|
||||
Defaults to :class:`.BoundField` class.
|
||||
|
||||
This can be customized further using :attr:`.Form.bound_field_class`
|
||||
for per-form overrides, or :attr:`.Field.bound_field_class` for
|
||||
per-field overrides.
|
||||
|
||||
.. method:: get_template(template_name)
|
||||
|
||||
Subclasses must implement this method with the appropriate template
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue