Refs #32339 -- Restructured outputting HTML form docs.
In the topic doc, promoted the Reusable form templates section. In the reference, re-grouped and promoted the default __str__() rendering path, and then gathered the various as_*() helpers subsequently. Thanks to David Smith for review.
This commit is contained in:
parent
5d91dc8ee3
commit
fde946daff
|
@ -520,9 +520,15 @@ Although ``<table>`` output is the default output style when you ``print`` a
|
||||||
form, other output styles are available. Each style is available as a method on
|
form, other output styles are available. Each style is available as a method on
|
||||||
a form object, and each rendering method returns a string.
|
a form object, and each rendering method returns a string.
|
||||||
|
|
||||||
``template_name``
|
Default rendering
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
The default rendering when you ``print`` a form uses the following methods and
|
||||||
|
attributes.
|
||||||
|
|
||||||
|
``template_name``
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. versionadded:: 4.0
|
.. versionadded:: 4.0
|
||||||
|
|
||||||
.. attribute:: Form.template_name
|
.. attribute:: Form.template_name
|
||||||
|
@ -540,95 +546,8 @@ class.
|
||||||
In older versions ``template_name`` defaulted to the string value
|
In older versions ``template_name`` defaulted to the string value
|
||||||
``'django/forms/default.html'``.
|
``'django/forms/default.html'``.
|
||||||
|
|
||||||
``template_name_label``
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
.. versionadded:: 4.0
|
|
||||||
|
|
||||||
.. attribute:: Form.template_name_label
|
|
||||||
|
|
||||||
The template used to render a field's ``<label>``, used when calling
|
|
||||||
:meth:`BoundField.label_tag`/:meth:`~BoundField.legend_tag`. Can be changed per
|
|
||||||
form by overriding this attribute or more generally by overriding the default
|
|
||||||
template, see also :ref:`overriding-built-in-form-templates`.
|
|
||||||
|
|
||||||
``as_p()``
|
|
||||||
----------
|
|
||||||
|
|
||||||
.. method:: Form.as_p()
|
|
||||||
|
|
||||||
``as_p()`` renders the form using the template assigned to the forms
|
|
||||||
``template_name_p`` attribute, by default this template is
|
|
||||||
``'django/forms/p.html'``. This template renders the form as a series of
|
|
||||||
``<p>`` tags, with each ``<p>`` containing one field::
|
|
||||||
|
|
||||||
>>> f = ContactForm()
|
|
||||||
>>> f.as_p()
|
|
||||||
'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" required></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>'
|
|
||||||
>>> print(f.as_p())
|
|
||||||
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>
|
|
||||||
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>
|
|
||||||
<p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></p>
|
|
||||||
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>
|
|
||||||
|
|
||||||
``as_ul()``
|
|
||||||
-----------
|
|
||||||
|
|
||||||
.. method:: Form.as_ul()
|
|
||||||
|
|
||||||
``as_ul()`` renders the form using the template assigned to the forms
|
|
||||||
``template_name_ul`` attribute, by default this template is
|
|
||||||
``'django/forms/ul.html'``. This template renders the form as a series of
|
|
||||||
``<li>`` tags, with each ``<li>`` containing one field. It does *not* include
|
|
||||||
the ``<ul>`` or ``</ul>``, so that you can specify any HTML attributes on the
|
|
||||||
``<ul>`` for flexibility::
|
|
||||||
|
|
||||||
>>> f = ContactForm()
|
|
||||||
>>> f.as_ul()
|
|
||||||
'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>'
|
|
||||||
>>> print(f.as_ul())
|
|
||||||
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>
|
|
||||||
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>
|
|
||||||
<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>
|
|
||||||
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>
|
|
||||||
|
|
||||||
``as_table()``
|
|
||||||
--------------
|
|
||||||
|
|
||||||
.. method:: Form.as_table()
|
|
||||||
|
|
||||||
Finally, ``as_table()`` renders the form using the template assigned to the
|
|
||||||
forms ``template_name_table`` attribute, by default this template is
|
|
||||||
``'django/forms/table.html'``. This template outputs the form as an HTML
|
|
||||||
``<table>``::
|
|
||||||
|
|
||||||
>>> f = ContactForm()
|
|
||||||
>>> f.as_table()
|
|
||||||
'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>'
|
|
||||||
>>> print(f)
|
|
||||||
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>
|
|
||||||
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>
|
|
||||||
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
|
|
||||||
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>
|
|
||||||
|
|
||||||
``get_context()``
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
.. versionadded:: 4.0
|
|
||||||
|
|
||||||
.. method:: Form.get_context()
|
|
||||||
|
|
||||||
Return context for form rendering in a template.
|
|
||||||
|
|
||||||
The available context is:
|
|
||||||
|
|
||||||
* ``form``: The bound form.
|
|
||||||
* ``fields``: All bound fields, except the hidden fields.
|
|
||||||
* ``hidden_fields``: All hidden bound fields.
|
|
||||||
* ``errors``: All non field related or hidden field related form errors.
|
|
||||||
|
|
||||||
``render()``
|
``render()``
|
||||||
------------
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
.. versionadded:: 4.0
|
.. versionadded:: 4.0
|
||||||
|
|
||||||
|
@ -642,6 +561,115 @@ All arguments are optional and default to:
|
||||||
* ``context``: Value returned by :meth:`.Form.get_context`
|
* ``context``: Value returned by :meth:`.Form.get_context`
|
||||||
* ``renderer``: Value returned by :attr:`.Form.default_renderer`
|
* ``renderer``: Value returned by :attr:`.Form.default_renderer`
|
||||||
|
|
||||||
|
By passing ``template_name`` you can customize the template used for just a
|
||||||
|
single call.
|
||||||
|
|
||||||
|
``get_context()``
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
|
||||||
|
.. method:: Form.get_context()
|
||||||
|
|
||||||
|
Return the template context for rendering the form.
|
||||||
|
|
||||||
|
The available context is:
|
||||||
|
|
||||||
|
* ``form``: The bound form.
|
||||||
|
* ``fields``: All bound fields, except the hidden fields.
|
||||||
|
* ``hidden_fields``: All hidden bound fields.
|
||||||
|
* ``errors``: All non field related or hidden field related form errors.
|
||||||
|
|
||||||
|
``template_name_label``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
|
||||||
|
.. attribute:: Form.template_name_label
|
||||||
|
|
||||||
|
The template used to render a field's ``<label>``, used when calling
|
||||||
|
:meth:`BoundField.label_tag`/:meth:`~BoundField.legend_tag`. Can be changed per
|
||||||
|
form by overriding this attribute or more generally by overriding the default
|
||||||
|
template, see also :ref:`overriding-built-in-form-templates`.
|
||||||
|
|
||||||
|
Output styles
|
||||||
|
-------------
|
||||||
|
|
||||||
|
As well as rendering the form directly, such as in a template with
|
||||||
|
``{{ form }}``, the following helper functions serve as a proxy to
|
||||||
|
:meth:`Form.render` passing a particular ``template_name`` value.
|
||||||
|
|
||||||
|
These helpers are most useful in a template, where you need to override the
|
||||||
|
form renderer or form provided value but cannot pass the additional parameter
|
||||||
|
to :meth:`~Form.render`. For example, you can render a form as an unordered
|
||||||
|
list using ``{{ form.as_ul }}``.
|
||||||
|
|
||||||
|
Each helper pairs a form method with an attribute giving the appropriate
|
||||||
|
template name.
|
||||||
|
|
||||||
|
``as_p()``
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
.. attribute:: Form.template_name_p
|
||||||
|
|
||||||
|
The template used by ``as_p()``. Default: ``'django/forms/p.html'``.
|
||||||
|
|
||||||
|
.. method:: Form.as_p()
|
||||||
|
|
||||||
|
``as_p()`` renders the form as a series of ``<p>`` tags, with each ``<p>``
|
||||||
|
containing one field::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.as_p()
|
||||||
|
'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" required></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>'
|
||||||
|
>>> print(f.as_p())
|
||||||
|
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>
|
||||||
|
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>
|
||||||
|
<p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></p>
|
||||||
|
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>
|
||||||
|
|
||||||
|
``as_ul()``
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. attribute:: Form.template_name_ul
|
||||||
|
|
||||||
|
The template used by ``as_ul()``. Default: ``'django/forms/ul.html'``.
|
||||||
|
|
||||||
|
.. method:: Form.as_ul()
|
||||||
|
|
||||||
|
``as_ul()`` renders the form as a series of ``<li>`` tags, with each ``<li>``
|
||||||
|
containing one field. It does *not* include the ``<ul>`` or ``</ul>``, so that
|
||||||
|
you can specify any HTML attributes on the ``<ul>`` for flexibility::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.as_ul()
|
||||||
|
'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>'
|
||||||
|
>>> print(f.as_ul())
|
||||||
|
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>
|
||||||
|
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>
|
||||||
|
<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>
|
||||||
|
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>
|
||||||
|
|
||||||
|
``as_table()``
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. attribute:: Form.template_name_table
|
||||||
|
|
||||||
|
The template used by ``as_table()``. Default: ``'django/forms/table.html'``.
|
||||||
|
|
||||||
|
.. method:: Form.as_table()
|
||||||
|
|
||||||
|
``as_table()`` renders the form as an HTML ``<table>``::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.as_table()
|
||||||
|
'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>'
|
||||||
|
>>> print(f)
|
||||||
|
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>
|
||||||
|
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>
|
||||||
|
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
|
||||||
|
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>
|
||||||
|
|
||||||
.. _ref-forms-api-styling-form-rows:
|
.. _ref-forms-api-styling-form-rows:
|
||||||
|
|
||||||
Styling required or erroneous form rows
|
Styling required or erroneous form rows
|
||||||
|
|
|
@ -487,15 +487,83 @@ instance into the template context. So if your form is called ``form`` in the
|
||||||
context, ``{{ form }}`` will render its ``<label>`` and ``<input>`` elements
|
context, ``{{ form }}`` will render its ``<label>`` and ``<input>`` elements
|
||||||
appropriately.
|
appropriately.
|
||||||
|
|
||||||
Form rendering options
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
.. admonition:: Additional form template furniture
|
.. admonition:: Additional form template furniture
|
||||||
|
|
||||||
Don't forget that a form's output does *not* include the surrounding
|
Don't forget that a form's output does *not* include the surrounding
|
||||||
``<form>`` tags, or the form's ``submit`` control. You will have to provide
|
``<form>`` tags, or the form's ``submit`` control. You will have to provide
|
||||||
these yourself.
|
these yourself.
|
||||||
|
|
||||||
|
Reusable form templates
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
The HTML output when rendering a form is itself generated via a template. You
|
||||||
|
can control this by creating an appropriate template file and setting a custom
|
||||||
|
:setting:`FORM_RENDERER` to use that
|
||||||
|
:attr:`~django.forms.renderers.BaseRenderer.form_template_name` site-wide. You
|
||||||
|
can also customize per-form by overriding the form's
|
||||||
|
:attr:`~django.forms.Form.template_name` attribute to render the form using the
|
||||||
|
custom template, or by passing the template name directly to
|
||||||
|
:meth:`.Form.render`.
|
||||||
|
|
||||||
|
The example below will result in ``{{ form }}`` being rendered as the output of
|
||||||
|
the ``form_snippet.html`` template.
|
||||||
|
|
||||||
|
In your templates:
|
||||||
|
|
||||||
|
.. code-block:: html+django
|
||||||
|
|
||||||
|
# In your template:
|
||||||
|
{{ form }}
|
||||||
|
|
||||||
|
# In form_snippet.html:
|
||||||
|
{% for field in form %}
|
||||||
|
<div class="fieldWrapper">
|
||||||
|
{{ field.errors }}
|
||||||
|
{{ field.label_tag }} {{ field }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
Then you can configure the :setting:`FORM_RENDERER` setting:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: settings.py
|
||||||
|
|
||||||
|
from django.forms.renderers import TemplatesSetting
|
||||||
|
|
||||||
|
class CustomFormRenderer(TemplatesSetting):
|
||||||
|
form_template_name = "form_snippet.html"
|
||||||
|
|
||||||
|
FORM_RENDERER = "project.settings.CustomFormRenderer"
|
||||||
|
|
||||||
|
… or for a single form::
|
||||||
|
|
||||||
|
class MyForm(forms.Form):
|
||||||
|
template_name = "form_snippet.html"
|
||||||
|
...
|
||||||
|
|
||||||
|
… or for a single render of a form instance, passing in the template name to
|
||||||
|
the :meth:`.Form.render`. Here's an example of this being used in a view::
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
form = MyForm()
|
||||||
|
rendered_form = form.render("form_snippet.html")
|
||||||
|
context = {'form': rendered_form}
|
||||||
|
return render(request, 'index.html', context)
|
||||||
|
|
||||||
|
See :ref:`ref-forms-api-outputting-html` for more details.
|
||||||
|
|
||||||
|
.. versionchanged:: 4.0
|
||||||
|
|
||||||
|
Template rendering of forms was added.
|
||||||
|
|
||||||
|
.. versionchanged:: 4.1
|
||||||
|
|
||||||
|
The ability to set the default ``form_template_name`` on the form renderer
|
||||||
|
was added.
|
||||||
|
|
||||||
|
Form rendering options
|
||||||
|
----------------------
|
||||||
|
|
||||||
There are other output options though for the ``<label>``/``<input>`` pairs:
|
There are other output options though for the ``<label>``/``<input>`` pairs:
|
||||||
|
|
||||||
* ``{{ form.as_table }}`` will render them as table cells wrapped in ``<tr>``
|
* ``{{ form.as_table }}`` will render them as table cells wrapped in ``<tr>``
|
||||||
|
@ -754,71 +822,6 @@ error in a hidden field is a sign of form tampering, since normal form
|
||||||
interaction won't alter them. However, you could easily insert some error
|
interaction won't alter them. However, you could easily insert some error
|
||||||
displays for those form errors, as well.
|
displays for those form errors, as well.
|
||||||
|
|
||||||
Reusable form templates
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
If your site uses the same rendering logic for forms in multiple places, you
|
|
||||||
can reduce duplication by saving the form's loop in a standalone template and
|
|
||||||
setting a custom :setting:`FORM_RENDERER` to use that
|
|
||||||
:attr:`~django.forms.renderers.BaseRenderer.form_template_name` site-wide. You
|
|
||||||
can also customize per-form by overriding the form's
|
|
||||||
:attr:`~django.forms.Form.template_name` attribute to render the form using the
|
|
||||||
custom template.
|
|
||||||
|
|
||||||
The below example will result in ``{{ form }}`` being rendered as the output of
|
|
||||||
the ``form_snippet.html`` template.
|
|
||||||
|
|
||||||
In your templates:
|
|
||||||
|
|
||||||
.. code-block:: html+django
|
|
||||||
|
|
||||||
# In your template:
|
|
||||||
{{ form }}
|
|
||||||
|
|
||||||
# In form_snippet.html:
|
|
||||||
{% for field in form %}
|
|
||||||
<div class="fieldWrapper">
|
|
||||||
{{ field.errors }}
|
|
||||||
{{ field.label_tag }} {{ field }}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
Then you can configure the :setting:`FORM_RENDERER` setting:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
:caption: settings.py
|
|
||||||
|
|
||||||
from django.forms.renderers import TemplatesSetting
|
|
||||||
|
|
||||||
class CustomFormRenderer(TemplatesSetting):
|
|
||||||
form_template_name = "form_snippet.html"
|
|
||||||
|
|
||||||
FORM_RENDERER = "project.settings.CustomFormRenderer"
|
|
||||||
|
|
||||||
… or for a single form::
|
|
||||||
|
|
||||||
class MyForm(forms.Form):
|
|
||||||
template_name = "form_snippet.html"
|
|
||||||
...
|
|
||||||
|
|
||||||
… or for a single render of a form instance, passing in the template name to
|
|
||||||
the :meth:`.Form.render()`. Here's an example of this being used in a view::
|
|
||||||
|
|
||||||
def index(request):
|
|
||||||
form = MyForm()
|
|
||||||
rendered_form = form.render("form_snippet.html")
|
|
||||||
context = {'form': rendered_form}
|
|
||||||
return render(request, 'index.html', context)
|
|
||||||
|
|
||||||
.. versionchanged:: 4.0
|
|
||||||
|
|
||||||
Template rendering of forms was added.
|
|
||||||
|
|
||||||
.. versionchanged:: 4.1
|
|
||||||
|
|
||||||
The ability to set the default ``form_template_name`` on the form renderer
|
|
||||||
was added.
|
|
||||||
|
|
||||||
Further topics
|
Further topics
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue