diff --git a/django/forms/forms.py b/django/forms/forms.py index 2500dccc9b..35471345c9 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -66,6 +66,7 @@ class BaseForm(RenderableFormMixin): prefix = None use_required_attribute = True + template_name_div = "django/forms/div.html" template_name_p = "django/forms/p.html" template_name_table = "django/forms/table.html" template_name_ul = "django/forms/ul.html" diff --git a/django/forms/formsets.py b/django/forms/formsets.py index d51b13548e..2df80297d3 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -63,6 +63,7 @@ class BaseFormSet(RenderableFormMixin): ), } + template_name_div = "django/forms/formsets/div.html" template_name_p = "django/forms/formsets/p.html" template_name_table = "django/forms/formsets/table.html" template_name_ul = "django/forms/formsets/ul.html" diff --git a/django/forms/jinja2/django/forms/div.html b/django/forms/jinja2/django/forms/div.html new file mode 100644 index 0000000000..6de0bb038e --- /dev/null +++ b/django/forms/jinja2/django/forms/div.html @@ -0,0 +1,24 @@ +{{ errors }} +{% if errors and not fields %} +
{% for field in hidden_fields %}{{ field }}{% endfor %}
+{% endif %} +{% for field, errors in fields %} + + {% if field.use_fieldset %} +
+ {% if field.label %}{{ field.legend_tag() }}{% endif %} + {% else %} + {% if field.label %}{{ field.label_tag() }}{% endif %} + {% endif %} + {% if field.help_text %}
{{ field.help_text|safe }}
{% endif %} + {{ errors }} + {{ field }} + {% if field.use_fieldset %}
{% endif %} + {% if loop.last %} + {% for field in hidden_fields %}{{ field }}{% endfor %} + {% endif %} + +{% endfor %} +{% if not fields and not errors %} + {% for field in hidden_fields %}{{ field }}{% endfor %} +{% endif %} diff --git a/django/forms/jinja2/django/forms/formsets/div.html b/django/forms/jinja2/django/forms/formsets/div.html new file mode 100644 index 0000000000..0dda779d3f --- /dev/null +++ b/django/forms/jinja2/django/forms/formsets/div.html @@ -0,0 +1 @@ +{{ formset.management_form }}{% for form in formset %}{{ form.as_div() }}{% endfor %} diff --git a/django/forms/templates/django/forms/div.html b/django/forms/templates/django/forms/div.html new file mode 100644 index 0000000000..0328fdf8d3 --- /dev/null +++ b/django/forms/templates/django/forms/div.html @@ -0,0 +1,24 @@ +{{ errors }} +{% if errors and not fields %} +
{% for field in hidden_fields %}{{ field }}{% endfor %}
+{% endif %} +{% for field, errors in fields %} + + {% if field.use_fieldset %} +
+ {% if field.label %}{{ field.legend_tag }}{% endif %} + {% else %} + {% if field.label %}{{ field.label_tag }}{% endif %} + {% endif %} + {% if field.help_text %}
{{ field.help_text|safe }}
{% endif %} + {{ errors }} + {{ field }} + {% if field.use_fieldset %}
{% endif %} + {% if forloop.last %} + {% for field in hidden_fields %}{{ field }}{% endfor %} + {% endif %} + +{% endfor %} +{% if not fields and not errors %} + {% for field in hidden_fields %}{{ field }}{% endfor %} +{% endif %} diff --git a/django/forms/templates/django/forms/formsets/div.html b/django/forms/templates/django/forms/formsets/div.html new file mode 100644 index 0000000000..93499897d4 --- /dev/null +++ b/django/forms/templates/django/forms/formsets/div.html @@ -0,0 +1 @@ +{{ formset.management_form }}{% for form in formset %}{{ form.as_div }}{% endfor %} diff --git a/django/forms/utils.py b/django/forms/utils.py index 7d3bb5ad48..77678054db 100644 --- a/django/forms/utils.py +++ b/django/forms/utils.py @@ -73,6 +73,10 @@ class RenderableFormMixin(RenderableMixin): """Render as
  • elements excluding the surrounding
      tag.""" return self.render(self.template_name_ul) + def as_div(self): + """Render as
      elements.""" + return self.render(self.template_name_div) + class RenderableErrorMixin(RenderableMixin): def as_json(self, escape_html=False): diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt index 8530af2611..72937bfa9a 100644 --- a/docs/ref/forms/api.txt +++ b/docs/ref/forms/api.txt @@ -607,6 +607,55 @@ list using ``{{ form.as_ul }}``. Each helper pairs a form method with an attribute giving the appropriate template name. +``as_div()`` +~~~~~~~~~~~~ + +.. attribute:: Form.template_name_div + +.. versionadded:: 4.1 + +The template used by ``as_div()``. Default: ``'django/forms/div.html'``. + +.. method:: Form.as_div() + +.. versionadded:: 4.1 + +``as_div()`` renders the form as a series of ``
      `` elements, with each +``
      `` containing one field, such as: + +.. code-block:: pycon + + >>> f = ContactForm() + >>> f.as_div() + +… gives HTML like: + +.. code-block:: html + +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      + +.. note:: + + Of the framework provided templates and output styles, ``as_div()`` is + recommended over the ``as_p()``, ``as_table()``, and ``as_ul()`` versions + as the template implements ``
      `` and ```` to group related + inputs and is easier for screen reader users to navigate. + ``as_p()`` ~~~~~~~~~~ diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index 2864d179b2..1bd3fe68b0 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -258,6 +258,15 @@ Forms :attr:`~django.forms.renderers.BaseRenderer.formset_template_name` renderer attribute. +* The new ``div.html`` form template, referencing + :attr:`.Form.template_name_div` attribute, and matching :meth:`.Form.as_div` + method, render forms using HTML ``
      `` elements. + + This new output style is recommended over the existing + :meth:`~.Form.as_table`, :meth:`~.Form.as_p` and :meth:`~.Form.as_ul` styles, + as the template implements ``
      `` and ```` to group related + inputs and is easier for screen reader users to navigate. + * The new :meth:`~django.forms.BoundField.legend_tag` allows rendering field labels in ```` tags via the new ``tag`` argument of :meth:`~django.forms.BoundField.label_tag`. diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt index 0206959f1d..1b54f2accb 100644 --- a/docs/topics/forms/formsets.txt +++ b/docs/topics/forms/formsets.txt @@ -800,12 +800,22 @@ Formsets have the following attributes and methods associated with rendering: In older versions ``template_name`` defaulted to the string value ``'django/forms/formset/default.html'``. + +.. attribute:: BaseFormSet.template_name_div + + .. versionadded:: 4.1 + + The name of the template used when calling :meth:`.as_div`. By default this + is ``"django/forms/formsets/div.html"``. This template renders the + formset's management form and then each form in the formset as per the + form's :meth:`~django.forms.Form.as_div` method. + .. 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 formset's + is ``"django/forms/formsets/p.html"``. This template renders the formset's management form and then each form in the formset as per the form's :meth:`~django.forms.Form.as_p` method. @@ -814,7 +824,7 @@ Formsets have the following attributes and methods associated with rendering: .. 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 + this is ``"django/forms/formsets/table.html"``. This template renders the formset's management form and then each form in the formset as per the form's :meth:`~django.forms.Form.as_table` method. @@ -823,7 +833,7 @@ Formsets have the following attributes and methods associated with rendering: .. 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 formset's + is ``"django/forms/formsets/ul.html"``. This template renders the formset's management form and then each form in the formset as per the form's :meth:`~django.forms.Form.as_ul` method. diff --git a/docs/topics/forms/index.txt b/docs/topics/forms/index.txt index c8e43389a0..e153d73bab 100644 --- a/docs/topics/forms/index.txt +++ b/docs/topics/forms/index.txt @@ -566,12 +566,14 @@ Form rendering options There are other output options though for the ``