summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorDavid Smith <smithdc@gmail.com>2021-09-10 08:06:01 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-09-20 15:50:18 +0200
commit456466d932830b096d39806e291fe23ec5ed38d5 (patch)
tree9320cc645ef43eb920630cff02c1387b34f21906 /docs
parent5353e7c2505c0d0ab8232ad9c131b3c99c833988 (diff)
Fixed #31026 -- Switched form rendering to template engine.
Thanks Carlton Gibson, Keryn Knight, Mariusz Felisiak, and Nick Pope for reviews. Co-authored-by: Johannes Hoppe <info@johanneshoppe.com>
Diffstat (limited to 'docs')
-rw-r--r--docs/internals/deprecation.txt5
-rw-r--r--docs/ref/forms/api.txt204
-rw-r--r--docs/ref/forms/formsets.txt6
-rw-r--r--docs/ref/forms/models.txt18
-rw-r--r--docs/ref/forms/renderers.txt59
-rw-r--r--docs/ref/settings.txt5
-rw-r--r--docs/releases/4.0.txt17
-rw-r--r--docs/topics/forms/formsets.txt95
-rw-r--r--docs/topics/forms/index.txt24
9 files changed, 377 insertions, 56 deletions
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index 23af7315dd..29af8cc7e2 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -57,6 +57,11 @@ details on these changes.
* The ``django.contrib.gis.admin.GeoModelAdmin`` and ``OSMGeoAdmin`` classes
will be removed.
+* The undocumented ``BaseForm._html_output()`` method will be removed.
+
+* The ability to return a ``str``, rather than a ``SafeString``, when rendering
+ an ``ErrorDict`` and ``ErrorList`` will be removed.
+
.. _deprecation-removed-in-4.1:
4.1
diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt
index 9cdbc21800..9dcfbcfb09 100644
--- a/docs/ref/forms/api.txt
+++ b/docs/ref/forms/api.txt
@@ -520,13 +520,41 @@ 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
a form object, and each rendering method returns a string.
+``template_name``
+-----------------
+
+.. versionadded:: 4.0
+
+.. attribute:: Form.template_name
+
+The name of a template that is going to be rendered if the form is cast into a
+string, e.g. via ``print(form)`` or in a template via ``{{ form }}``. By
+default this template is ``'django/forms/default.html'``, which is a proxy for
+``'django/forms/table.html'``. The template can be changed per form by
+overriding the ``template_name`` attribute or more generally by overriding the
+default template, see also :ref:`overriding-built-in-form-templates`.
+
+``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`. 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 as a series of ``<p>`` tags, with each ``<p>``
-containing one field::
+``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()
@@ -542,10 +570,12 @@ containing one field::
.. 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::
+``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()
@@ -561,9 +591,10 @@ flexibility::
.. method:: Form.as_table()
-Finally, ``as_table()`` outputs the form as an HTML ``<table>``. This is
-exactly the same as ``print``. In fact, when you ``print`` a form object,
-it calls its ``as_table()`` method behind the scenes::
+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()
@@ -574,6 +605,37 @@ it calls its ``as_table()`` method behind the scenes::
<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()``
+------------
+
+.. versionadded:: 4.0
+
+.. method:: Form.render(template_name=None, context=None, renderer=None)
+
+The render method is called by ``__str__`` as well as the
+:meth:`.Form.as_table`, :meth:`.Form.as_p`, and :meth:`.Form.as_ul` methods.
+All arguments are optional and default to:
+
+* ``template_name``: :attr:`.Form.template_name`
+* ``context``: Value returned by :meth:`.Form.get_context`
+* ``renderer``: Value returned by :attr:`.Form.default_renderer`
+
.. _ref-forms-api-styling-form-rows:
Styling required or erroneous form rows
@@ -834,25 +896,99 @@ method you're using::
Customizing the error list format
---------------------------------
-By default, forms use ``django.forms.utils.ErrorList`` to format validation
-errors. If you'd like to use an alternate class for displaying errors, you can
-pass that in at construction time::
+.. class:: ErrorList(initlist=None, error_class=None, renderer=None)
- >>> from django.forms.utils import ErrorList
- >>> class DivErrorList(ErrorList):
- ... def __str__(self):
- ... return self.as_divs()
- ... def as_divs(self):
- ... if not self: return ''
- ... return '<div class="errorlist">%s</div>' % ''.join(['<div class="error">%s</div>' % e for e in self])
- >>> f = ContactForm(data, auto_id=False, error_class=DivErrorList)
- >>> f.as_p()
- <div class="errorlist"><div class="error">This field is required.</div></div>
- <p>Subject: <input type="text" name="subject" maxlength="100" required></p>
- <p>Message: <input type="text" name="message" value="Hi there" required></p>
- <div class="errorlist"><div class="error">Enter a valid email address.</div></div>
- <p>Sender: <input type="email" name="sender" value="invalid email address" required></p>
- <p>Cc myself: <input checked type="checkbox" name="cc_myself"></p>
+ By default, forms use ``django.forms.utils.ErrorList`` to format validation
+ errors. ``ErrorList`` is a list like object where ``initlist`` is the
+ list of errors. In addition this class has the following attributes and
+ methods.
+
+ .. attribute:: error_class
+
+ The CSS classes to be used when rendering the error list. Any provided
+ classes are added to the default ``errorlist`` class.
+
+ .. attribute:: renderer
+
+ .. versionadded:: 4.0
+
+ Specifies the :doc:`renderer <renderers>` to use for ``ErrorList``.
+ Defaults to ``None`` which means to use the default renderer
+ specified by the :setting:`FORM_RENDERER` setting.
+
+ .. attribute:: template_name
+
+ .. versionadded:: 4.0
+
+ The name of the template used when calling ``__str__`` or
+ :meth:`render`. By default this is
+ ``'django/forms/errors/list/default.html'`` which is a proxy for the
+ ``'ul.html'`` template.
+
+ .. attribute:: template_name_text
+
+ .. versionadded:: 4.0
+
+ The name of the template used when calling :meth:`.as_text`. By default
+ this is ``'django/forms/errors/list/text.html'``. This template renders
+ the errors as a list of bullet points.
+
+ .. attribute:: template_name_ul
+
+ .. versionadded:: 4.0
+
+ The name of the template used when calling :meth:`.as_ul`. By default
+ this is ``'django/forms/errors/list/ul.html'``. This template renders
+ the errors in ``<li>`` tags with a wrapping ``<ul>`` with the CSS
+ classes as defined by :attr:`.error_class`.
+
+ .. method:: get_context()
+
+ .. versionadded:: 4.0
+
+ Return context for rendering of errors in a template.
+
+ The available context is:
+
+ * ``errors`` : A list of the errors.
+ * ``error_class`` : A string of CSS classes.
+
+ .. method:: render(template_name=None, context=None, renderer=None)
+
+ .. versionadded:: 4.0
+
+ The render method is called by ``__str__`` as well as by the
+ :meth:`.as_ul` method.
+
+ All arguments are optional and will default to:
+
+ * ``template_name``: Value returned by :attr:`.template_name`
+ * ``context``: Value returned by :meth:`.get_context`
+ * ``renderer``: Value returned by :attr:`.renderer`
+
+ .. method:: as_text()
+
+ Renders the error list using the template defined by
+ :attr:`.template_name_text`.
+
+ .. method:: as_ul()
+
+ Renders the error list using the template defined by
+ :attr:`.template_name_ul`.
+
+ If you'd like to customize the rendering of errors this can be achieved by
+ overriding the :attr:`.template_name` attribute or more generally by
+ overriding the default template, see also
+ :ref:`overriding-built-in-form-templates`.
+
+.. versionchanged:: 4.0
+
+ Rendering of :class:`ErrorList` was moved to the template engine.
+
+.. deprecated:: 4.0
+
+ The ability to return a ``str`` when calling the ``__str__`` method is
+ deprecated. Use the template engine instead which returns a ``SafeString``.
More granular output
====================
@@ -1086,12 +1222,16 @@ Methods of ``BoundField``
attributes for the ``<label>`` tag.
The HTML that's generated includes the form's
- :attr:`~django.forms.Form.label_suffix` (a colon, by default) or, if set, the
- current field's :attr:`~django.forms.Field.label_suffix`. The optional
+ :attr:`~django.forms.Form.label_suffix` (a colon, by default) or, if set,
+ the current field's :attr:`~django.forms.Field.label_suffix`. The optional
``label_suffix`` parameter allows you to override any previously set
- suffix. For example, you can use an empty string to hide the label on selected
- fields. If you need to do this in a template, you could write a custom
- filter to allow passing parameters to ``label_tag``.
+ suffix. For example, you can use an empty string to hide the label on
+ selected fields. The label is rendered using the template specified by the
+ forms :attr:`.Form.template_name_label`.
+
+ .. versionchanged:: 4.0
+
+ The label is now rendered using the template engine.
.. method:: BoundField.value()
diff --git a/docs/ref/forms/formsets.txt b/docs/ref/forms/formsets.txt
index 0e281f2f59..677184df05 100644
--- a/docs/ref/forms/formsets.txt
+++ b/docs/ref/forms/formsets.txt
@@ -11,7 +11,7 @@ Formset API reference. For introductory material about formsets, see the
``formset_factory``
===================
-.. function:: formset_factory(form, formset=BaseFormSet, extra=1, can_order=False, can_delete=False, max_num=None, validate_max=False, min_num=None, validate_min=False, absolute_max=None, can_delete_extra=True)
+.. function:: formset_factory(form, formset=BaseFormSet, extra=1, can_order=False, can_delete=False, max_num=None, validate_max=False, min_num=None, validate_min=False, absolute_max=None, can_delete_extra=True, renderer=None)
Returns a ``FormSet`` class for the given ``form`` class.
@@ -20,3 +20,7 @@ Formset API reference. For introductory material about formsets, see the
.. versionchanged:: 3.2
The ``absolute_max`` and ``can_delete_extra`` arguments were added.
+
+ .. versionchanged:: 4.0
+
+ The ``renderer`` argument was added.
diff --git a/docs/ref/forms/models.txt b/docs/ref/forms/models.txt
index 13ed69887e..59eac83e7d 100644
--- a/docs/ref/forms/models.txt
+++ b/docs/ref/forms/models.txt
@@ -52,7 +52,7 @@ Model Form API reference. For introductory material about model forms, see the
``modelformset_factory``
========================
-.. function:: modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None, min_num=None, validate_min=False, field_classes=None, absolute_max=None, can_delete_extra=True)
+.. function:: modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None, min_num=None, validate_min=False, field_classes=None, absolute_max=None, can_delete_extra=True, renderer=None)
Returns a ``FormSet`` class for the given ``model`` class.
@@ -63,9 +63,9 @@ Model Form API reference. For introductory material about model forms, see the
Arguments ``formset``, ``extra``, ``can_delete``, ``can_order``,
``max_num``, ``validate_max``, ``min_num``, ``validate_min``,
- ``absolute_max``, and ``can_delete_extra`` are passed through to
- :func:`~django.forms.formsets.formset_factory`. See :doc:`formsets
- </topics/forms/formsets>` for details.
+ ``absolute_max``, ``can_delete_extra``, and ``renderer`` are passed
+ through to :func:`~django.forms.formsets.formset_factory`. See
+ :doc:`formsets </topics/forms/formsets>` for details.
See :ref:`model-formsets` for example usage.
@@ -73,10 +73,14 @@ Model Form API reference. For introductory material about model forms, see the
The ``absolute_max`` and ``can_delete_extra`` arguments were added.
+ .. versionchanged:: 4.0
+
+ The ``renderer`` argument was added.
+
``inlineformset_factory``
=========================
-.. function:: inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None, min_num=None, validate_min=False, field_classes=None, absolute_max=None, can_delete_extra=True)
+.. function:: inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None, min_num=None, validate_min=False, field_classes=None, absolute_max=None, can_delete_extra=True, renderer=None)
Returns an ``InlineFormSet`` using :func:`modelformset_factory` with
defaults of ``formset=``:class:`~django.forms.models.BaseInlineFormSet`,
@@ -90,3 +94,7 @@ Model Form API reference. For introductory material about model forms, see the
.. versionchanged:: 3.2
The ``absolute_max`` and ``can_delete_extra`` arguments were added.
+
+ .. versionchanged:: 4.0
+
+ The ``renderer`` argument was added.
diff --git a/docs/ref/forms/renderers.txt b/docs/ref/forms/renderers.txt
index 58caa08c32..77ee33d662 100644
--- a/docs/ref/forms/renderers.txt
+++ b/docs/ref/forms/renderers.txt
@@ -68,11 +68,11 @@ it uses a :class:`~django.template.backends.jinja2.Jinja2` backend. Templates
for the built-in widgets are located in ``django/forms/jinja2`` and installed
apps can provide templates in a ``jinja2`` directory.
-To use this backend, all the widgets in your project and its third-party apps
-must have Jinja2 templates. Unless you provide your own Jinja2 templates for
-widgets that don't have any, you can't use this renderer. For example,
-:mod:`django.contrib.admin` doesn't include Jinja2 templates for its widgets
-due to their usage of Django template tags.
+To use this backend, all the forms and widgets in your project and its
+third-party apps must have Jinja2 templates. Unless you provide your own Jinja2
+templates for widgets that don't have any, you can't use this renderer. For
+example, :mod:`django.contrib.admin` doesn't include Jinja2 templates for its
+widgets due to their usage of Django template tags.
``TemplatesSetting``
--------------------
@@ -97,6 +97,29 @@ Using this renderer along with the built-in widget templates requires either:
Using this renderer requires you to make sure the form templates your project
needs can be located.
+Context available in formset templates
+======================================
+
+.. versionadded:: 4.0
+
+Formset templates receive a context from :meth:`.BaseFormSet.get_context`. By
+default, formsets receive a dictionary with the following values:
+
+* ``formset``: The formset instance.
+
+Context available in form templates
+===================================
+
+.. versionadded:: 4.0
+
+Form templates receive a context from :meth:`.Form.get_context`. By default,
+forms receive a dictionary with the following values:
+
+* ``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.
+
Context available in widget templates
=====================================
@@ -114,6 +137,32 @@ Some widgets add further information to the context. For instance, all widgets
that subclass ``Input`` defines ``widget['type']`` and :class:`.MultiWidget`
defines ``widget['subwidgets']`` for looping purposes.
+.. _overriding-built-in-formset-templates:
+
+Overriding built-in formset templates
+=====================================
+
+.. versionadded:: 4.0
+
+:attr:`.BaseFormSet.template_name`
+
+To override formset templates, you must use the :class:`TemplatesSetting`
+renderer. Then overriding widget templates works :doc:`the same as
+</howto/overriding-templates>` overriding any other template in your project.
+
+.. _overriding-built-in-form-templates:
+
+Overriding built-in form templates
+==================================
+
+.. versionadded:: 4.0
+
+:attr:`.Form.template_name`
+
+To override form templates, you must use the :class:`TemplatesSetting`
+renderer. Then overriding widget templates works :doc:`the same as
+</howto/overriding-templates>` overriding any other template in your project.
+
.. _overriding-built-in-widget-templates:
Overriding built-in widget templates
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
index f534b8746c..a272bdbcb3 100644
--- a/docs/ref/settings.txt
+++ b/docs/ref/settings.txt
@@ -1671,8 +1671,9 @@ generate correct URLs when ``SCRIPT_NAME`` is not ``/``.
Default: ``'``:class:`django.forms.renderers.DjangoTemplates`\ ``'``
-The class that renders form widgets. It must implement :ref:`the low-level
-render API <low-level-widget-render-api>`. Included form renderers are:
+The class that renders forms and form widgets. It must implement
+:ref:`the low-level render API <low-level-widget-render-api>`. Included form
+renderers are:
* ``'``:class:`django.forms.renderers.DjangoTemplates`\ ``'``
* ``'``:class:`django.forms.renderers.Jinja2`\ ``'``
diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt
index de7ec8d93e..33abb0365e 100644
--- a/docs/releases/4.0.txt
+++ b/docs/releases/4.0.txt
@@ -115,6 +115,17 @@ in Django <redis>`.
.. _`redis-py`: https://pypi.org/project/redis/
+Template based form rendering
+-----------------------------
+
+To enhance customization of :class:`Forms <django.forms.Form>`,
+:doc:`Formsets </topics/forms/formsets>`, and
+:class:`~django.forms.ErrorList` they are now rendered using the template
+engine. See the new :meth:`~django.forms.Form.render`,
+:meth:`~django.forms.Form.get_context`, and
+:attr:`~django.forms.Form.template_name` for ``Form`` and
+:ref:`formset rendering <formset-rendering>` for ``Formset``.
+
Minor features
--------------
@@ -735,6 +746,12 @@ Miscellaneous
are deprecated. Use :class:`~django.contrib.admin.ModelAdmin` and
:class:`~django.contrib.gis.admin.GISModelAdmin` instead.
+* Since form rendering now uses the template engine, the undocumented
+ ``BaseForm._html_output()`` helper method is deprecated.
+
+* The ability to return a ``str`` from ``ErrorList`` and ``ErrorDict`` is
+ deprecated. It is expected these methods return a ``SafeString``.
+
Features removed in 4.0
=======================
diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt
index 30979edae4..e15f11dea0 100644
--- a/docs/topics/forms/formsets.txt
+++ b/docs/topics/forms/formsets.txt
@@ -775,9 +775,92 @@ But with ``ArticleFormset(prefix='article')`` that becomes:
This is useful if you want to :ref:`use more than one formset in a view
<multiple-formsets-in-view>`.
+.. _formset-rendering:
+
Using a formset in views and templates
======================================
+Formsets have five attributes and five methods associated with rendering.
+
+.. attribute:: BaseFormSet.renderer
+
+ .. versionadded:: 4.0
+
+ Specifies the :doc:`renderer </ref/forms/renderers>` to use for the
+ formset. Defaults to the renderer specified by the :setting:`FORM_RENDERER`
+ setting.
+
+.. attribute:: BaseFormSet.template_name
+
+ .. versionadded:: 4.0
+
+ The name of the template used when calling ``__str__`` or :meth:`.render`.
+ This template renders the formsets management forms and then each form in
+ the formset as per the template defined by the
+ forms :attr:`~django.forms.Form.template_name`. This is a proxy of
+ ``as_table`` by default.
+
+.. attribute:: BaseFormSet.template_name_p
+
+ .. versionadded:: 4.0
+
+ The name of the template used when calling :meth:`.as_p`. By default this
+ is ``'django/forms/formsets/p.html'``. This template renders the formsets
+ management forms and then each form in the formset as per the forms
+ :meth:`~django.forms.Form.as_p` method.
+
+.. attribute:: BaseFormSet.template_name_table
+
+ .. versionadded:: 4.0
+
+ The name of the template used when calling :meth:`.as_table`. By default
+ this is ``'django/forms/formsets/table.html'``. This template renders the
+ formsets management forms and then each form in the formset as per the
+ forms :meth:`~django.forms.Form.as_table` method.
+
+.. attribute:: BaseFormSet.template_name_ul
+
+ .. versionadded:: 4.0
+
+ The name of the template used when calling :meth:`.as_ul`. By default this
+ is ``'django/forms/formsets/ul.html'``. This template renders the formsets
+ management forms and then each form in the formset as per the forms
+ :meth:`~django.forms.Form.as_ul` method.
+
+.. method:: BaseFormSet.get_context()
+
+ .. versionadded:: 4.0
+
+ Returns the context for rendering a formset in a template.
+
+ The available context is:
+
+ * ``formset`` : The instance of the formset.
+
+.. method:: BaseFormSet.render(template_name=None, context=None, renderer=None)
+
+ .. versionadded:: 4.0
+
+ The render method is called by ``__str__`` as well as the :meth:`.as_p`,
+ :meth:`.as_ul`, and :meth:`.as_table` methods. All arguments are optional
+ and will default to:
+
+ * ``template_name``: :attr:`.template_name`
+ * ``context``: Value returned by :meth:`.get_context`
+ * ``renderer``: Value returned by :attr:`.renderer`
+
+.. method:: BaseFormSet.as_p()
+
+ Renders the formset with the :attr:`.template_name_p` template.
+
+.. method:: BaseFormSet.as_table()
+
+ Renders the formset with the :attr:`.template_name_table` template.
+
+.. method:: BaseFormSet.as_ul()
+
+ Renders the formset with the :attr:`.template_name_ul` template.
+
Using a formset inside a view is not very different from using a regular
``Form`` class. The only thing you will want to be aware of is making sure to
use the management form inside the template. Let's look at a sample view::
@@ -821,7 +904,17 @@ deal with the management form:
</table>
</form>
-The above ends up calling the ``as_table`` method on the formset class.
+The above ends up calling the :meth:`BaseFormSet.render` method on the formset
+class. This renders the formset using the template specified by the
+:attr:`~BaseFormSet.template_name` attribute. Similar to forms, by default the
+formset will be rendered ``as_table``, with other helper methods of ``as_p``
+and ``as_ul`` being available. The rendering of the formset can be customized
+by specifying the ``template_name`` attribute, or more generally by
+:ref:`overriding the default template <overriding-built-in-formset-templates>`.
+
+.. versionchanged:: 4.0
+
+ Rendering of formsets was moved to the template engine.
.. _manually-rendered-can-delete-and-can-order:
diff --git a/docs/topics/forms/index.txt b/docs/topics/forms/index.txt
index eed18a2cee..8ed99d5773 100644
--- a/docs/topics/forms/index.txt
+++ b/docs/topics/forms/index.txt
@@ -733,12 +733,17 @@ 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
-using the :ttag:`include` tag to reuse it in other templates:
+overriding the forms :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 form template:
- {% include "form_snippet.html" %}
+ # In your template:
+ {{ form }}
# In form_snippet.html:
{% for field in form %}
@@ -748,16 +753,15 @@ using the :ttag:`include` tag to reuse it in other templates:
</div>
{% endfor %}
-If the form object passed to a template has a different name within the
-context, you can alias it using the ``with`` argument of the :ttag:`include`
-tag:
+In your form::
-.. code-block:: html+django
+ class MyForm(forms.Form):
+ template_name = 'form_snippet.html'
+ ...
- {% include "form_snippet.html" with form=comment_form %}
+.. versionchanged:: 4.0
-If you find yourself doing this often, you might consider creating a custom
-:ref:`inclusion tag<howto-custom-template-tags-inclusion-tags>`.
+ Template rendering of forms was added.
Further topics
==============