Fixed #11185 - Expanded docs on customizing widgets; thanks fadeev for the draft patch.

This commit is contained in:
Tim Graham 2012-09-12 06:59:19 -04:00
parent 65793d714c
commit a73838fde3
3 changed files with 214 additions and 78 deletions

View File

@ -852,7 +852,7 @@ Slightly complex built-in ``Field`` classes
``MultiValueField``
~~~~~~~~~~~~~~~~~~~
.. class:: MultiValueField(**kwargs)
.. class:: MultiValueField(fields=(), **kwargs)
* Default widget: ``TextInput``
* Empty value: ``''`` (an empty string)
@ -861,22 +861,39 @@ Slightly complex built-in ``Field`` classes
as an argument to the ``MultiValueField``.
* Error message keys: ``required``, ``invalid``
This abstract field (must be subclassed) aggregates the logic of multiple
fields. Subclasses should not have to implement clean(). Instead, they must
implement compress(), which takes a list of valid values and returns a
"compressed" version of those values -- a single value. For example,
:class:`SplitDateTimeField` is a subclass which combines a time field and
a date field into a datetime object.
Aggregates the logic of multiple fields that together produce a single
value.
This field is abstract and must be subclassed. In contrast with the
single-value fields, subclasses of :class:`MultiValueField` must not
implement :meth:`~django.forms.Field.clean` but instead - implement
:meth:`~MultiValueField.compress`.
Takes one extra required argument:
.. attribute:: fields
A list of fields which are cleaned into a single field. Each value in
``clean`` is cleaned by the corresponding field in ``fields`` -- the first
value is cleaned by the first field, the second value is cleaned by
the second field, etc. Once all fields are cleaned, the list of clean
values is "compressed" into a single value.
A tuple of fields whose values are cleaned and subsequently combined
into a single value. Each value of the field is cleaned by the
corresponding field in ``fields`` -- the first value is cleaned by the
first field, the second value is cleaned by the second field, etc.
Once all fields are cleaned, the list of clean values is combined into
a single value by :meth:`~MultiValueField.compress`.
.. attribute:: MultiValueField.widget
Must be a subclass of :class:`django.forms.MultiWidget`.
Default value is :class:`~django.forms.widgets.TextInput`, which
probably is not very useful in this case.
.. method:: compress(data_list)
Takes a list of valid values and returns a "compressed" version of
those values -- in a single value. For example,
:class:`SplitDateTimeField` is a subclass which combines a time field
and a date field into a ``datetime`` object.
This method must be implemented in the subclasses.
``SplitDateTimeField``
~~~~~~~~~~~~~~~~~~~~~~

View File

@ -11,6 +11,16 @@ A widget is Django's representation of a HTML input element. The widget
handles the rendering of the HTML, and the extraction of data from a GET/POST
dictionary that corresponds to the widget.
.. tip::
Widgets should not be confused with the :doc:`form fields </ref/forms/fields>`.
Form fields deal with the logic of input validation and are used directly
in templates. Widgets deal with rendering of HTML form input elements on
the web page and extraction of raw submitted data. However, widgets do
need to be :ref:`assigned <widget-to-field>` to form fields.
.. _widget-to-field:
Specifying widgets
------------------
@ -95,15 +105,23 @@ choices are inherent to the model and not just the representational widget.
Customizing widget instances
----------------------------
When Django renders a widget as HTML, it only renders the bare minimum
HTML - Django doesn't add a class definition, or any other widget-specific
attributes. This means that all :class:`TextInput` widgets will appear the same
on your Web page.
When Django renders a widget as HTML, it only renders very minimal markup -
Django doesn't add class names, or any other widget-specific attributes. This
means, for example, that all :class:`TextInput` widgets will appear the same
on your Web pages.
If you want to make one widget look different to another, you need to
specify additional attributes for each widget. When you specify a
widget, you can provide a list of attributes that will be added to the
rendered HTML for the widget.
There are two ways to customize widgets: :ref:`per widget instance
<styling-widget-instances>` and :ref:`per widget class <styling-widget-classes>`.
.. _styling-widget-instances:
Styling widget instances
^^^^^^^^^^^^^^^^^^^^^^^^
If you want to make one widget instance look different from another, you will
need to specify additional attributes at the time when the widget object is
instantiated and assigned to a form field (and perhaps add some rules to your
CSS files).
For example, take the following simple form::
@ -128,9 +146,7 @@ On a real Web page, you probably don't want every widget to look the same. You
might want a larger input element for the comment, and you might want the
'name' widget to have some special CSS class. It is also possible to specify
the 'type' attribute to take advantage of the new HTML5 input types. To do
this, you use the :attr:`Widget.attrs` argument when creating the widget:
For example::
this, you use the :attr:`Widget.attrs` argument when creating the widget::
class CommentForm(forms.Form):
name = forms.CharField(
@ -147,24 +163,41 @@ Django will then include the extra attributes in the rendered output:
<tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>
.. _built-in widgets:
.. _styling-widget-classes:
Built-in widgets
----------------
Styling widget classes
^^^^^^^^^^^^^^^^^^^^^^
Django provides a representation of all the basic HTML widgets, plus some
commonly used groups of widgets:
With widgets, it is possible to add media (``css`` and ``javascript``)
and more deeply customize their appearance and behavior.
``Widget``
~~~~~~~~~~
In a nutshell, you will need to subclass the widget and either
:ref:`define a class "Media" <media-as-a-static-definition>` as a member of the
subclass, or :ref:`create a property "media" <dynamic-property>`, returning an
instance of that class.
.. class:: Widget
These methods involve somewhat advanced Python programming and are described in
detail in the :doc:`Form Media </topics/forms/media>` topic guide.
This abstract class cannot be rendered, but provides the basic attribute :attr:`~Widget.attrs`.
.. _base-widget-classes:
Base Widget classes
-------------------
Base widget classes :class:`Widget` and :class:`MultiWidget` are subclassed by
all the :ref:`built-in widgets <built-in widgets>` and may serve as a
foundation for custom widgets.
.. class:: Widget(attrs=None)
This abstract class cannot be rendered, but provides the basic attribute
:attr:`~Widget.attrs`. You may also implement or override the
:meth:`~Widget.render()` method on custom widgets.
.. attribute:: Widget.attrs
A dictionary containing HTML attributes to be set on the rendered widget.
A dictionary containing HTML attributes to be set on the rendered
widget.
.. code-block:: python
@ -172,6 +205,74 @@ commonly used groups of widgets:
>>> name.render('name', 'A name')
u'<input title="Your name" type="text" name="name" value="A name" size="10" />'
.. method:: render(name, value, attrs=None)
Returns HTML for the widget, as a Unicode string. This method must be
implemented by the subclass, otherwise ``NotImplementedError`` will be
raised.
The 'value' given is not guaranteed to be valid input, therefore
subclass implementations should program defensively.
.. class:: MultiWidget(widgets, attrs=None)
A widget that is composed of multiple widgets.
:class:`~django.forms.widgets.MultiWidget` works hand in hand with the
:class:`~django.forms.MultiValueField`.
.. method:: render(name, value, attrs=None)
Argument `value` is handled differently in this method from the
subclasses of :class:`~Widget`.
If `value` is a list, output of :meth:`~MultiWidget.render` will be a
concatenation of rendered child widgets. If `value` is not a list, it
will be first processed by the method :meth:`~MultiWidget.decompress()`
to create the list and then processed as above.
Unlike in the single value widgets, method :meth:`~MultiWidget.render`
need not be implemented in the subclasses.
.. method:: decompress(value)
Returns a list of "decompressed" values for the given value of the
multi-value field that makes use of the widget. The input value can be
assumed as valid, but not necessarily non-empty.
This method **must be implemented** by the subclass, and since the
value may be empty, the implementation must be defensive.
The rationale behind "decompression" is that it is necessary to "split"
the combined value of the form field into the values of the individual
field encapsulated within the multi-value field (e.g. when displaying
the partially or fully filled-out form).
.. tip::
Note that :class:`~django.forms.MultiValueField` has a
complementary method :meth:`~django.forms.MultiValueField.compress`
with the opposite responsibility - to combine cleaned values of
all member fields into one.
.. _built-in widgets:
Built-in widgets
----------------
Django provides a representation of all the basic HTML widgets, plus some
commonly used groups of widgets in the ``django.forms.widgets`` module,
including :ref:`the input of text <text-widgets>`, :ref:`various checkboxes
and selectors <selector-widgets>`, :ref:`uploading files <file-upload-widgets>`,
and :ref:`handling of multi-valued input <composite-widgets>`.
.. _text-widgets:
Widgets handling input of text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
These widgets make use of the HTML elements ``input`` and ``textarea``.
``TextInput``
~~~~~~~~~~~~~
@ -205,39 +306,8 @@ commonly used groups of widgets:
Hidden input: ``<input type='hidden' ...>``
``MultipleHiddenInput``
~~~~~~~~~~~~~~~~~~~~~~~
.. class:: MultipleHiddenInput
Multiple ``<input type='hidden' ...>`` widgets.
A widget that handles multiple hidden widgets for fields that have a list
of values.
.. attribute:: MultipleHiddenInput.choices
This attribute is optional when the field does not have a
:attr:`~Field.choices` attribute. If it does, it will override anything
you set here when the attribute is updated on the :class:`Field`.
``FileInput``
~~~~~~~~~~~~~
.. class:: FileInput
File upload input: ``<input type='file' ...>``
``ClearableFileInput``
~~~~~~~~~~~~~~~~~~~~~~
.. class:: ClearableFileInput
.. versionadded:: 1.3
File upload input: ``<input type='file' ...>``, with an additional checkbox
input to clear the field's value, if the field is not required and has
initial data.
Note that there also is a :class:`MultipleHiddenInput` widget that
encapsulates a set of hidden input elements.
``DateInput``
~~~~~~~~~~~~~
@ -297,6 +367,11 @@ commonly used groups of widgets:
Text area: ``<textarea>...</textarea>``
.. _selector-widgets:
Selector and checkbox widgets
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``CheckboxInput``
~~~~~~~~~~~~~~~~~
@ -440,6 +515,50 @@ commonly used groups of widgets:
...
</ul>
.. _file-upload-widgets:
File upload widgets
^^^^^^^^^^^^^^^^^^^
``FileInput``
~~~~~~~~~~~~~
.. class:: FileInput
File upload input: ``<input type='file' ...>``
``ClearableFileInput``
~~~~~~~~~~~~~~~~~~~~~~
.. class:: ClearableFileInput
.. versionadded:: 1.3
File upload input: ``<input type='file' ...>``, with an additional checkbox
input to clear the field's value, if the field is not required and has
initial data.
.. _composite-widgets:
Composite widgets
^^^^^^^^^^^^^^^^^
``MultipleHiddenInput``
~~~~~~~~~~~~~~~~~~~~~~~
.. class:: MultipleHiddenInput
Multiple ``<input type='hidden' ...>`` widgets.
A widget that handles multiple hidden widgets for fields that have a list
of values.
.. attribute:: MultipleHiddenInput.choices
This attribute is optional when the field does not have a
:attr:`~Field.choices` attribute. If it does, it will override anything
you set here when the attribute is updated on the :class:`Field`.
``MultiWidget``
~~~~~~~~~~~~~~~

View File

@ -38,6 +38,8 @@ in a form suitable for easy inclusion on your Web page.
whichever toolkit suits your requirements. Django is able to integrate
with any JavaScript toolkit.
.. _media-as-a-static-definition:
Media as a static definition
----------------------------
@ -78,10 +80,8 @@ A dictionary describing the CSS files required for various forms of output
media.
The values in the dictionary should be a tuple/list of file names. See
`the section on media paths`_ for details of how to specify paths to media
files.
.. _the section on media paths: `Paths in media definitions`_
:ref:`the section on media paths <form-media-paths>` for details of how to
specify paths to media files.
The keys in the dictionary are the output media types. These are the same
types accepted by CSS files in media declarations: 'all', 'aural', 'braille',
@ -117,8 +117,8 @@ If this last CSS definition were to be rendered, it would become the following H
``js``
~~~~~~
A tuple describing the required JavaScript files. See
`the section on media paths`_ for details of how to specify paths to media
A tuple describing the required JavaScript files. See :ref:`the section on
media paths <form-media-paths>` for details of how to specify paths to media
files.
``extend``
@ -164,10 +164,10 @@ declaration to the media declaration::
<script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
If you require even more control over media inheritance, define your media
using a `dynamic property`_. Dynamic properties give you complete control over
which media files are inherited, and which are not.
using a :ref:`dynamic property <dynamic-property>`. Dynamic properties give
you complete control over which media files are inherited, and which are not.
.. _dynamic property: `Media as a dynamic property`_
.. _dynamic-property:
Media as a dynamic property
---------------------------
@ -198,9 +198,9 @@ Paths in media definitions
.. versionchanged:: 1.3
Paths used to specify media can be either relative or absolute. If a path
starts with '/', 'http://' or 'https://', it will be interpreted as an absolute
path, and left as-is. All other paths will be prepended with the value of
the appropriate prefix.
starts with ``/``, ``http://`` or ``https://``, it will be interpreted as an
absolute path, and left as-is. All other paths will be prepended with the value
of the appropriate prefix.
As part of the introduction of the
:doc:`staticfiles app </ref/contrib/staticfiles>` two new settings were added