Refs #32339 -- Allowed renderer to specify default form and formset templates.
Co-authored-by: David Smith <smithdc@gmail.com>
This commit is contained in:
parent
832096478c
commit
476d4d5087
|
@ -66,7 +66,6 @@ class BaseForm(RenderableFormMixin):
|
|||
prefix = None
|
||||
use_required_attribute = True
|
||||
|
||||
template_name = "django/forms/default.html"
|
||||
template_name_p = "django/forms/p.html"
|
||||
template_name_table = "django/forms/table.html"
|
||||
template_name_ul = "django/forms/ul.html"
|
||||
|
@ -316,6 +315,10 @@ class BaseForm(RenderableFormMixin):
|
|||
output.append(str_hidden)
|
||||
return mark_safe("\n".join(output))
|
||||
|
||||
@property
|
||||
def template_name(self):
|
||||
return self.renderer.form_template_name
|
||||
|
||||
def get_context(self):
|
||||
fields = []
|
||||
hidden_fields = []
|
||||
|
|
|
@ -62,7 +62,7 @@ class BaseFormSet(RenderableFormMixin):
|
|||
"%(field_names)s. You may need to file a bug report if the issue persists."
|
||||
),
|
||||
}
|
||||
template_name = "django/forms/formsets/default.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"
|
||||
|
@ -517,6 +517,10 @@ class BaseFormSet(RenderableFormMixin):
|
|||
else:
|
||||
return self.empty_form.media
|
||||
|
||||
@property
|
||||
def template_name(self):
|
||||
return self.renderer.formset_template_name
|
||||
|
||||
def get_context(self):
|
||||
return {"formset": self}
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@ def get_default_renderer():
|
|||
|
||||
|
||||
class BaseRenderer:
|
||||
form_template_name = "django/forms/default.html"
|
||||
formset_template_name = "django/forms/formsets/default.html"
|
||||
|
||||
def get_template(self, template_name):
|
||||
raise NotImplementedError("subclasses must implement get_template()")
|
||||
|
||||
|
|
|
@ -527,12 +527,18 @@ a form object, and each rendering method returns a string.
|
|||
|
||||
.. 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`.
|
||||
The name of the template rendered if the form is cast into a string, e.g. via
|
||||
``print(form)`` or in a template via ``{{ form }}``.
|
||||
|
||||
By default, a property returning the value of the renderer's
|
||||
:attr:`~django.forms.renderers.BaseRenderer.form_template_name`. You may set it
|
||||
as a string template name in order to override that for a particular form
|
||||
class.
|
||||
|
||||
.. versionchanged:: 4.1
|
||||
|
||||
In older versions ``template_name`` defaulted to the string value
|
||||
``'django/forms/default.html'``.
|
||||
|
||||
``template_name_label``
|
||||
-----------------------
|
||||
|
|
|
@ -26,9 +26,16 @@ A custom renderer can be specified by updating the :setting:`FORM_RENDERER`
|
|||
setting. It defaults to
|
||||
``'``:class:`django.forms.renderers.DjangoTemplates`\ ``'``.
|
||||
|
||||
You can also provide a custom renderer by setting the
|
||||
By specifying a custom form renderer and overriding
|
||||
:attr:`~.BaseRenderer.form_template_name` you can adjust the default form
|
||||
markup across your project from a single place.
|
||||
|
||||
You can also provide a custom renderer per-form or per-widget by setting the
|
||||
:attr:`.Form.default_renderer` attribute or by using the ``renderer`` argument
|
||||
of :meth:`.Widget.render`.
|
||||
of :meth:`.Form.render`, or :meth:`.Widget.render`.
|
||||
|
||||
Matching points apply to formset rendering. See :ref:`formset-rendering` for
|
||||
discussion.
|
||||
|
||||
Use one of the :ref:`built-in template form renderers
|
||||
<built-in-template-form-renderers>` or implement your own. Custom renderers
|
||||
|
@ -40,6 +47,24 @@ should return a rendered templates (as a string) or raise
|
|||
|
||||
The base class for the built-in form renderers.
|
||||
|
||||
.. attribute:: form_template_name
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
The default name of the template to use to render a form.
|
||||
|
||||
Defaults to ``"django/forms/default.html"``, which is a proxy for
|
||||
``"django/forms/table.html"``.
|
||||
|
||||
.. attribute:: formset_template_name
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
The default name of the template to use to render a formset.
|
||||
|
||||
Defaults to ``"django/forms/formsets/default.html"``, which is a proxy
|
||||
for ``"django/forms/formsets/table.html"``.
|
||||
|
||||
.. method:: get_template(template_name)
|
||||
|
||||
Subclasses must implement this method with the appropriate template
|
||||
|
|
|
@ -244,6 +244,20 @@ File Uploads
|
|||
Forms
|
||||
~~~~~
|
||||
|
||||
* The default template used to render forms when cast to a string, e.g. in
|
||||
templates as ``{{ form }}``, is now configurable at the project-level by
|
||||
setting :attr:`~django.forms.renderers.BaseRenderer.form_template_name` on
|
||||
the class provided for :setting:`FORM_RENDERER`.
|
||||
|
||||
:attr:`.Form.template_name` is now a property deferring to the renderer, but
|
||||
may be overridden with a string value to specify the template name per-form
|
||||
class.
|
||||
|
||||
Similarly, the default template used to render formsets can be specified via
|
||||
the matching
|
||||
:attr:`~django.forms.renderers.BaseRenderer.formset_template_name` renderer
|
||||
attribute.
|
||||
|
||||
* The new :meth:`~django.forms.BoundField.legend_tag` allows rendering field
|
||||
labels in ``<legend>`` tags via the new ``tag`` argument of
|
||||
:meth:`~django.forms.BoundField.label_tag`.
|
||||
|
|
|
@ -783,11 +783,22 @@ Formsets have the following attributes and methods associated with rendering:
|
|||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
The name of the template used when calling ``__str__`` or :meth:`.render`.
|
||||
This template renders the formset's management form and then each form in
|
||||
the formset as per the template defined by the form's
|
||||
:attr:`~django.forms.Form.template_name`. This is a proxy of ``as_table``
|
||||
by default.
|
||||
The name of the template rendered if the formset is cast into a string,
|
||||
e.g. via ``print(formset)`` or in a template via ``{{ formset }}``.
|
||||
|
||||
By default, a property returning the value of the renderer's
|
||||
:attr:`~django.forms.renderers.BaseRenderer.formset_template_name`. You may
|
||||
set it as a string template name in order to override that for a particular
|
||||
formset class.
|
||||
|
||||
This template will be used to render the formset's management form, and
|
||||
then each form in the formset as per the template defined by the form's
|
||||
:attr:`~django.forms.Form.template_name`.
|
||||
|
||||
.. versionchanged:: 4.1
|
||||
|
||||
In older versions ``template_name`` defaulted to the string value
|
||||
``'django/forms/formset/default.html'``.
|
||||
|
||||
.. attribute:: BaseFormSet.template_name_p
|
||||
|
||||
|
|
|
@ -759,10 +759,14 @@ 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
|
||||
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.
|
||||
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:
|
||||
|
||||
|
@ -779,16 +783,42 @@ In your templates:
|
|||
</div>
|
||||
{% endfor %}
|
||||
|
||||
In your form::
|
||||
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'
|
||||
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
|
||||
==============
|
||||
|
||||
|
|
|
@ -4397,7 +4397,7 @@ class Jinja2FormsTestCase(FormsTestCase):
|
|||
|
||||
|
||||
class CustomRenderer(DjangoTemplates):
|
||||
pass
|
||||
form_template_name = "forms_tests/form_snippet.html"
|
||||
|
||||
|
||||
class RendererTests(SimpleTestCase):
|
||||
|
@ -4813,7 +4813,22 @@ class TemplateTests(SimpleTestCase):
|
|||
|
||||
|
||||
class OverrideTests(SimpleTestCase):
|
||||
def test_use_custom_template(self):
|
||||
@override_settings(FORM_RENDERER="forms_tests.tests.test_forms.CustomRenderer")
|
||||
def test_custom_renderer_template_name(self):
|
||||
class Person(Form):
|
||||
first_name = CharField()
|
||||
|
||||
get_default_renderer.cache_clear()
|
||||
t = Template("{{ form }}")
|
||||
html = t.render(Context({"form": Person()}))
|
||||
expected = """
|
||||
<div class="fieldWrapper"><label for="id_first_name">First name:</label>
|
||||
<input type="text" name="first_name" required id="id_first_name"></div>
|
||||
"""
|
||||
self.assertHTMLEqual(html, expected)
|
||||
get_default_renderer.cache_clear()
|
||||
|
||||
def test_per_form_template_name(self):
|
||||
class Person(Form):
|
||||
first_name = CharField()
|
||||
template_name = "forms_tests/form_snippet.html"
|
||||
|
|
|
@ -23,6 +23,7 @@ from django.forms.formsets import (
|
|||
all_valid,
|
||||
formset_factory,
|
||||
)
|
||||
from django.forms.renderers import TemplatesSetting
|
||||
from django.forms.utils import ErrorList
|
||||
from django.forms.widgets import HiddenInput
|
||||
from django.test import SimpleTestCase
|
||||
|
@ -1435,6 +1436,26 @@ class FormsFormsetTestCase(SimpleTestCase):
|
|||
self.assertIs(formset._should_delete_form(formset.forms[1]), False)
|
||||
self.assertIs(formset._should_delete_form(formset.forms[2]), False)
|
||||
|
||||
def test_template_name_uses_renderer_value(self):
|
||||
class CustomRenderer(TemplatesSetting):
|
||||
formset_template_name = "a/custom/formset/template.html"
|
||||
|
||||
ChoiceFormSet = formset_factory(Choice, renderer=CustomRenderer)
|
||||
|
||||
self.assertEqual(
|
||||
ChoiceFormSet().template_name, "a/custom/formset/template.html"
|
||||
)
|
||||
|
||||
def test_template_name_can_be_overridden(self):
|
||||
class CustomFormSet(BaseFormSet):
|
||||
template_name = "a/custom/formset/template.html"
|
||||
|
||||
ChoiceFormSet = formset_factory(Choice, formset=CustomFormSet)
|
||||
|
||||
self.assertEqual(
|
||||
ChoiceFormSet().template_name, "a/custom/formset/template.html"
|
||||
)
|
||||
|
||||
def test_custom_renderer(self):
|
||||
"""
|
||||
A custom renderer passed to a formset_factory() is passed to all forms
|
||||
|
|
Loading…
Reference in New Issue