Skip to content

Commit fde946d

Browse files
committed
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.
1 parent 5d91dc8 commit fde946d

File tree

2 files changed

+149
-118
lines changed

2 files changed

+149
-118
lines changed

docs/ref/forms/api.txt

Lines changed: 78 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -520,9 +520,15 @@ Although ``<table>`` output is the default output style when you ``print`` a
520520
form, other output styles are available. Each style is available as a method on
521521
a form object, and each rendering method returns a string.
522522

523-
``template_name``
523+
Default rendering
524524
-----------------
525525

526+
The default rendering when you ``print`` a form uses the following methods and
527+
attributes.
528+
529+
``template_name``
530+
~~~~~~~~~~~~~~~~~
531+
526532
.. versionadded:: 4.0
527533

528534
.. attribute:: Form.template_name
@@ -540,8 +546,42 @@ class.
540546
In older versions ``template_name`` defaulted to the string value
541547
``'django/forms/default.html'``.
542548

549+
``render()``
550+
~~~~~~~~~~~~
551+
552+
.. versionadded:: 4.0
553+
554+
.. method:: Form.render(template_name=None, context=None, renderer=None)
555+
556+
The render method is called by ``__str__`` as well as the
557+
:meth:`.Form.as_table`, :meth:`.Form.as_p`, and :meth:`.Form.as_ul` methods.
558+
All arguments are optional and default to:
559+
560+
* ``template_name``: :attr:`.Form.template_name`
561+
* ``context``: Value returned by :meth:`.Form.get_context`
562+
* ``renderer``: Value returned by :attr:`.Form.default_renderer`
563+
564+
By passing ``template_name`` you can customize the template used for just a
565+
single call.
566+
567+
``get_context()``
568+
~~~~~~~~~~~~~~~~~
569+
570+
.. versionadded:: 4.0
571+
572+
.. method:: Form.get_context()
573+
574+
Return the template context for rendering the form.
575+
576+
The available context is:
577+
578+
* ``form``: The bound form.
579+
* ``fields``: All bound fields, except the hidden fields.
580+
* ``hidden_fields``: All hidden bound fields.
581+
* ``errors``: All non field related or hidden field related form errors.
582+
543583
``template_name_label``
544-
-----------------------
584+
~~~~~~~~~~~~~~~~~~~~~~~
545585

546586
.. versionadded:: 4.0
547587

@@ -552,15 +592,32 @@ The template used to render a field's ``<label>``, used when calling
552592
form by overriding this attribute or more generally by overriding the default
553593
template, see also :ref:`overriding-built-in-form-templates`.
554594

595+
Output styles
596+
-------------
597+
598+
As well as rendering the form directly, such as in a template with
599+
``{{ form }}``, the following helper functions serve as a proxy to
600+
:meth:`Form.render` passing a particular ``template_name`` value.
601+
602+
These helpers are most useful in a template, where you need to override the
603+
form renderer or form provided value but cannot pass the additional parameter
604+
to :meth:`~Form.render`. For example, you can render a form as an unordered
605+
list using ``{{ form.as_ul }}``.
606+
607+
Each helper pairs a form method with an attribute giving the appropriate
608+
template name.
609+
555610
``as_p()``
556-
----------
611+
~~~~~~~~~~
612+
613+
.. attribute:: Form.template_name_p
614+
615+
The template used by ``as_p()``. Default: ``'django/forms/p.html'``.
557616

558617
.. method:: Form.as_p()
559618

560-
``as_p()`` renders the form using the template assigned to the forms
561-
``template_name_p`` attribute, by default this template is
562-
``'django/forms/p.html'``. This template renders the form as a series of
563-
``<p>`` tags, with each ``<p>`` containing one field::
619+
``as_p()`` renders the form as a series of ``<p>`` tags, with each ``<p>``
620+
containing one field::
564621

565622
>>> f = ContactForm()
566623
>>> f.as_p()
@@ -572,16 +629,17 @@ template, see also :ref:`overriding-built-in-form-templates`.
572629
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>
573630

574631
``as_ul()``
575-
-----------
632+
~~~~~~~~~~~
633+
634+
.. attribute:: Form.template_name_ul
635+
636+
The template used by ``as_ul()``. Default: ``'django/forms/ul.html'``.
576637

577638
.. method:: Form.as_ul()
578639

579-
``as_ul()`` renders the form using the template assigned to the forms
580-
``template_name_ul`` attribute, by default this template is
581-
``'django/forms/ul.html'``. This template renders the form as a series of
582-
``<li>`` tags, with each ``<li>`` containing one field. It does *not* include
583-
the ``<ul>`` or ``</ul>``, so that you can specify any HTML attributes on the
584-
``<ul>`` for flexibility::
640+
``as_ul()`` renders the form as a series of ``<li>`` tags, with each ``<li>``
641+
containing one field. It does *not* include the ``<ul>`` or ``</ul>``, so that
642+
you can specify any HTML attributes on the ``<ul>`` for flexibility::
585643

586644
>>> f = ContactForm()
587645
>>> f.as_ul()
@@ -593,14 +651,15 @@ the ``<ul>`` or ``</ul>``, so that you can specify any HTML attributes on the
593651
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>
594652

595653
``as_table()``
596-
--------------
654+
~~~~~~~~~~~~~~
655+
656+
.. attribute:: Form.template_name_table
657+
658+
The template used by ``as_table()``. Default: ``'django/forms/table.html'``.
597659

598660
.. method:: Form.as_table()
599661

600-
Finally, ``as_table()`` renders the form using the template assigned to the
601-
forms ``template_name_table`` attribute, by default this template is
602-
``'django/forms/table.html'``. This template outputs the form as an HTML
603-
``<table>``::
662+
``as_table()`` renders the form as an HTML ``<table>``::
604663

605664
>>> f = ContactForm()
606665
>>> f.as_table()
@@ -611,37 +670,6 @@ forms ``template_name_table`` attribute, by default this template is
611670
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
612671
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>
613672

614-
``get_context()``
615-
-----------------
616-
617-
.. versionadded:: 4.0
618-
619-
.. method:: Form.get_context()
620-
621-
Return context for form rendering in a template.
622-
623-
The available context is:
624-
625-
* ``form``: The bound form.
626-
* ``fields``: All bound fields, except the hidden fields.
627-
* ``hidden_fields``: All hidden bound fields.
628-
* ``errors``: All non field related or hidden field related form errors.
629-
630-
``render()``
631-
------------
632-
633-
.. versionadded:: 4.0
634-
635-
.. method:: Form.render(template_name=None, context=None, renderer=None)
636-
637-
The render method is called by ``__str__`` as well as the
638-
:meth:`.Form.as_table`, :meth:`.Form.as_p`, and :meth:`.Form.as_ul` methods.
639-
All arguments are optional and default to:
640-
641-
* ``template_name``: :attr:`.Form.template_name`
642-
* ``context``: Value returned by :meth:`.Form.get_context`
643-
* ``renderer``: Value returned by :attr:`.Form.default_renderer`
644-
645673
.. _ref-forms-api-styling-form-rows:
646674

647675
Styling required or erroneous form rows

docs/topics/forms/index.txt

Lines changed: 71 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -487,15 +487,83 @@ instance into the template context. So if your form is called ``form`` in the
487487
context, ``{{ form }}`` will render its ``<label>`` and ``<input>`` elements
488488
appropriately.
489489

490-
Form rendering options
491-
----------------------
492-
493490
.. admonition:: Additional form template furniture
494491

495492
Don't forget that a form's output does *not* include the surrounding
496493
``<form>`` tags, or the form's ``submit`` control. You will have to provide
497494
these yourself.
498495

496+
Reusable form templates
497+
-----------------------
498+
499+
The HTML output when rendering a form is itself generated via a template. You
500+
can control this by creating an appropriate template file and setting a custom
501+
:setting:`FORM_RENDERER` to use that
502+
:attr:`~django.forms.renderers.BaseRenderer.form_template_name` site-wide. You
503+
can also customize per-form by overriding the form's
504+
:attr:`~django.forms.Form.template_name` attribute to render the form using the
505+
custom template, or by passing the template name directly to
506+
:meth:`.Form.render`.
507+
508+
The example below will result in ``{{ form }}`` being rendered as the output of
509+
the ``form_snippet.html`` template.
510+
511+
In your templates:
512+
513+
.. code-block:: html+django
514+
515+
# In your template:
516+
{{ form }}
517+
518+
# In form_snippet.html:
519+
{% for field in form %}
520+
<div class="fieldWrapper">
521+
{{ field.errors }}
522+
{{ field.label_tag }} {{ field }}
523+
</div>
524+
{% endfor %}
525+
526+
Then you can configure the :setting:`FORM_RENDERER` setting:
527+
528+
.. code-block:: python
529+
:caption: settings.py
530+
531+
from django.forms.renderers import TemplatesSetting
532+
533+
class CustomFormRenderer(TemplatesSetting):
534+
form_template_name = "form_snippet.html"
535+
536+
FORM_RENDERER = "project.settings.CustomFormRenderer"
537+
538+
… or for a single form::
539+
540+
class MyForm(forms.Form):
541+
template_name = "form_snippet.html"
542+
...
543+
544+
… or for a single render of a form instance, passing in the template name to
545+
the :meth:`.Form.render`. Here's an example of this being used in a view::
546+
547+
def index(request):
548+
form = MyForm()
549+
rendered_form = form.render("form_snippet.html")
550+
context = {'form': rendered_form}
551+
return render(request, 'index.html', context)
552+
553+
See :ref:`ref-forms-api-outputting-html` for more details.
554+
555+
.. versionchanged:: 4.0
556+
557+
Template rendering of forms was added.
558+
559+
.. versionchanged:: 4.1
560+
561+
The ability to set the default ``form_template_name`` on the form renderer
562+
was added.
563+
564+
Form rendering options
565+
----------------------
566+
499567
There are other output options though for the ``<label>``/``<input>`` pairs:
500568

501569
* ``{{ 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
754822
interaction won't alter them. However, you could easily insert some error
755823
displays for those form errors, as well.
756824

757-
Reusable form templates
758-
-----------------------
759-
760-
If your site uses the same rendering logic for forms in multiple places, you
761-
can reduce duplication by saving the form's loop in a standalone template and
762-
setting a custom :setting:`FORM_RENDERER` to use that
763-
:attr:`~django.forms.renderers.BaseRenderer.form_template_name` site-wide. You
764-
can also customize per-form by overriding the form's
765-
:attr:`~django.forms.Form.template_name` attribute to render the form using the
766-
custom template.
767-
768-
The below example will result in ``{{ form }}`` being rendered as the output of
769-
the ``form_snippet.html`` template.
770-
771-
In your templates:
772-
773-
.. code-block:: html+django
774-
775-
# In your template:
776-
{{ form }}
777-
778-
# In form_snippet.html:
779-
{% for field in form %}
780-
<div class="fieldWrapper">
781-
{{ field.errors }}
782-
{{ field.label_tag }} {{ field }}
783-
</div>
784-
{% endfor %}
785-
786-
Then you can configure the :setting:`FORM_RENDERER` setting:
787-
788-
.. code-block:: python
789-
:caption: settings.py
790-
791-
from django.forms.renderers import TemplatesSetting
792-
793-
class CustomFormRenderer(TemplatesSetting):
794-
form_template_name = "form_snippet.html"
795-
796-
FORM_RENDERER = "project.settings.CustomFormRenderer"
797-
798-
… or for a single form::
799-
800-
class MyForm(forms.Form):
801-
template_name = "form_snippet.html"
802-
...
803-
804-
… or for a single render of a form instance, passing in the template name to
805-
the :meth:`.Form.render()`. Here's an example of this being used in a view::
806-
807-
def index(request):
808-
form = MyForm()
809-
rendered_form = form.render("form_snippet.html")
810-
context = {'form': rendered_form}
811-
return render(request, 'index.html', context)
812-
813-
.. versionchanged:: 4.0
814-
815-
Template rendering of forms was added.
816-
817-
.. versionchanged:: 4.1
818-
819-
The ability to set the default ``form_template_name`` on the form renderer
820-
was added.
821-
822825
Further topics
823826
==============
824827

0 commit comments

Comments
 (0)