From b52c73008a9d67e9ddbb841872dc15cdd3d6ee01 Mon Sep 17 00:00:00 2001 From: Preston Timmons Date: Tue, 27 Dec 2016 17:00:56 -0500 Subject: Fixed #15667 -- Added template-based widget rendering. Thanks Carl Meyer and Tim Graham for contributing to the patch. --- docs/ref/forms/api.txt | 23 +++++++ docs/ref/forms/index.txt | 1 + docs/ref/forms/renderers.txt | 131 ++++++++++++++++++++++++++++++++++++ docs/ref/forms/widgets.txt | 156 ++++++++++++++++++++++++++----------------- 4 files changed, 251 insertions(+), 60 deletions(-) create mode 100644 docs/ref/forms/renderers.txt (limited to 'docs/ref/forms') diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt index 194c390484..c74cfaed48 100644 --- a/docs/ref/forms/api.txt +++ b/docs/ref/forms/api.txt @@ -720,6 +720,29 @@ When set to ``True`` (the default), required form fields will have the ``use_required_attribute=False`` to avoid incorrect browser validation when adding and deleting forms from a formset. +Configuring the rendering of a form's widgets +--------------------------------------------- + +.. attribute:: Form.default_renderer + +.. versionadded:: 1.11 + +Specifies the :doc:`renderer ` to use for the form. Defaults to +``None`` which means to use the default renderer specified by the +:setting:`FORM_RENDERER` setting. + +You can set this as a class attribute when declaring your form or use the +``renderer`` argument to ``Form.__init__()``. For example:: + + from django import forms + + class MyForm(forms.Form): + default_renderer = MyRenderer() + +or:: + + form = MyForm(renderer=MyRenderer()) + Notes on field ordering ----------------------- diff --git a/docs/ref/forms/index.txt b/docs/ref/forms/index.txt index 618c705b92..241f979954 100644 --- a/docs/ref/forms/index.txt +++ b/docs/ref/forms/index.txt @@ -12,5 +12,6 @@ Detailed form API reference. For introductory material, see the fields models formsets + renderers widgets validation diff --git a/docs/ref/forms/renderers.txt b/docs/ref/forms/renderers.txt new file mode 100644 index 0000000000..c94b5ef226 --- /dev/null +++ b/docs/ref/forms/renderers.txt @@ -0,0 +1,131 @@ +====================== +The form rendering API +====================== + +.. module:: django.forms.renderers + :synopsis: Built-in form renderers. + +.. versionadded:: 1.11 + + In older versions, widgets are rendered using Python. All APIs described + in this document are new. + +Django's form widgets are rendered using Django's :doc:`template engines +system `. + +The form rendering process can be customized at several levels: + +* Widgets can specify custom template names. +* Forms and widgets can specify custom renderer classes. +* A widget's template can be overridden by a project. (Reusable applications + typically shouldn't override built-in templates because they might conflict + with a project's custom templates.) + +.. _low-level-widget-render-api: + +The low-level render API +======================== + +The rendering of form templates is controlled by a customizable renderer class. +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 +:attr:`.Form.default_renderer` attribute or by using the ``renderer`` argument +of :meth:`.Widget.render`. + +Use one of the :ref:`built-in template form renderers +` or implement your own. Custom renderers +must implement a ``render(template_name, context, request=None)`` method. It +should return a rendered templates (as a string) or raise +:exc:`~django.template.TemplateDoesNotExist`. + +.. _built-in-template-form-renderers: + +Built-in-template form renderers +================================ + +``DjangoTemplates`` +------------------- + +.. class:: DjangoTemplates + +This renderer uses a standalone +:class:`~django.template.backends.django.DjangoTemplates` +engine (unconnected to what you might have configured in the +:setting:`TEMPLATES` setting). It loads templates first from the built-in form +templates directory in ``django/forms/templates`` and then from the installed +apps' templates directories using the :class:`app_directories +` loader. + +If you want to render templates with customizations from your +:setting:`TEMPLATES` setting, such as context processors for example, use the +:class:`TemplatesSetting` renderer. + +``Jinja2`` +---------- + +.. class:: Jinja2 + +This renderer is the same as the :class:`DjangoTemplates` renderer except that +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. + +``TemplatesSetting`` +-------------------- + +.. class:: TemplatesSetting + +This renderer gives you complete control of how widget templates are sourced. +It uses :func:`~django.template.loader.get_template` to find widget +templates based on what's configured in the :setting:`TEMPLATES` setting. + +Using this renderer along with the built-in widget templates requires either: + +#. ``'django.forms'`` in :setting:`INSTALLED_APPS` and at least one engine + with :setting:`APP_DIRS=True `. + +#. Adding the built-in widgets templates directory (``django/forms/templates`` + or ``django/forms/jinja2``) in :setting:`DIRS ` of one of + your template engines. + +Using this renderer requires you to make sure the form templates your project +needs can be located. + +Context available in widget templates +===================================== + +Widget templates receive a context from :meth:`.Widget.get_context`. By +default, widgets receive a single value in the context, ``widget``. This is a +dictionary that contains values like: + +* ``name`` +* ``value`` +* ``attrs`` +* ``is_hidden`` +* ``template_name`` + +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 widget templates +==================================== + +Each widget has a ``template_name`` attribute with a value such as +``input.html``. Built-in widget templates are stored in the +``django/forms/widgets`` path. You can provide a custom template for +``input.html`` by defining ``django/forms/widgets/input.html``, for example. +See :ref:`built-in widgets` for the name of each widget's template. + +If you use the :class:`TemplatesSetting` renderer, overriding widget templates +works the same as overriding any other template in your project. You can't +override built-in widget templates using the other built-in renderers. diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt index c6148a5460..f0301c1eee 100644 --- a/docs/ref/forms/widgets.txt +++ b/docs/ref/forms/widgets.txt @@ -241,6 +241,28 @@ foundation for custom widgets. In older versions, this method is a private API named ``_format_value()``. The old name will work until Django 2.0. + .. method:: get_context(name, value, attrs=None) + + .. versionadded:: 1.11 + + Returns a dictionary of values to use when rendering the widget + template. By default, the dictionary contains a single key, + ``'widget'``, which is a dictionary representation of the widget + containing the following keys: + + * ``'name'``: The name of the field from the ``name`` argument. + * ``'is_hidden'``: A boolean indicating whether or not this widget is + hidden. + * ``'required'``: A boolean indicating whether or not the field for + this widget is required. + * ``'value'``: The value as returned by :meth:`format_value`. + * ``'attrs'``: HTML attributes to be set on the rendered widget. The + combination of the :attr:`attrs` attribute and the ``attrs`` argument. + * ``'template_name'``: The value of ``self.template_name``. + + ``Widget`` subclasses can provide custom context values by overriding + this method. + .. method:: id_for_label(self, id_) Returns the HTML ID attribute of this widget for use by a ``