mirror of https://github.com/django/django.git
Fixed #13997 - Added an example of constructing a MultiWidget and documented the value_from_datadict method.
This commit is contained in:
parent
ccb2b574e8
commit
04775b4598
|
@ -214,38 +214,49 @@ foundation for custom widgets.
|
||||||
The 'value' given is not guaranteed to be valid input, therefore
|
The 'value' given is not guaranteed to be valid input, therefore
|
||||||
subclass implementations should program defensively.
|
subclass implementations should program defensively.
|
||||||
|
|
||||||
|
.. method:: value_from_datadict(self, data, files, name)
|
||||||
|
|
||||||
|
Given a dictionary of data and this widget's name, returns the value
|
||||||
|
of this widget. Returns ``None`` if a value wasn't provided.
|
||||||
|
|
||||||
.. class:: MultiWidget(widgets, attrs=None)
|
.. class:: MultiWidget(widgets, attrs=None)
|
||||||
|
|
||||||
A widget that is composed of multiple widgets.
|
A widget that is composed of multiple widgets.
|
||||||
:class:`~django.forms.widgets.MultiWidget` works hand in hand with the
|
:class:`~django.forms.widgets.MultiWidget` works hand in hand with the
|
||||||
:class:`~django.forms.MultiValueField`.
|
:class:`~django.forms.MultiValueField`.
|
||||||
|
|
||||||
.. method:: render(name, value, attrs=None)
|
:class:`MultiWidget` has one required argument:
|
||||||
|
|
||||||
Argument `value` is handled differently in this method from the
|
.. attribute:: MultiWidget.widgets
|
||||||
subclasses of :class:`~Widget`.
|
|
||||||
|
|
||||||
If `value` is a list, output of :meth:`~MultiWidget.render` will be a
|
An iterable containing the widgets needed.
|
||||||
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`
|
And one required method:
|
||||||
need not be implemented in the subclasses.
|
|
||||||
|
|
||||||
.. method:: decompress(value)
|
.. method:: decompress(value)
|
||||||
|
|
||||||
Returns a list of "decompressed" values for the given value of the
|
This method takes a single "compressed" value from the field and
|
||||||
multi-value field that makes use of the widget. The input value can be
|
returns a list of "decompressed" values. The input value can be
|
||||||
assumed as valid, but not necessarily non-empty.
|
assumed valid, but not necessarily non-empty.
|
||||||
|
|
||||||
This method **must be implemented** by the subclass, and since the
|
This method **must be implemented** by the subclass, and since the
|
||||||
value may be empty, the implementation must be defensive.
|
value may be empty, the implementation must be defensive.
|
||||||
|
|
||||||
The rationale behind "decompression" is that it is necessary to "split"
|
The rationale behind "decompression" is that it is necessary to "split"
|
||||||
the combined value of the form field into the values of the individual
|
the combined value of the form field into the values for each widget.
|
||||||
field encapsulated within the multi-value field (e.g. when displaying
|
|
||||||
the partially or fully filled-out form).
|
An example of this is how :class:`SplitDateTimeWidget` turns a
|
||||||
|
:class:`datetime` value into a list with date and time split into two
|
||||||
|
separate values::
|
||||||
|
|
||||||
|
class SplitDateTimeWidget(MultiWidget):
|
||||||
|
|
||||||
|
# ...
|
||||||
|
|
||||||
|
def decompress(self, value):
|
||||||
|
if value:
|
||||||
|
return [value.date(), value.time().replace(microsecond=0)]
|
||||||
|
return [None, None]
|
||||||
|
|
||||||
.. tip::
|
.. tip::
|
||||||
|
|
||||||
|
@ -254,6 +265,109 @@ foundation for custom widgets.
|
||||||
with the opposite responsibility - to combine cleaned values of
|
with the opposite responsibility - to combine cleaned values of
|
||||||
all member fields into one.
|
all member fields into one.
|
||||||
|
|
||||||
|
Other methods that may be useful to override include:
|
||||||
|
|
||||||
|
.. method:: render(name, value, attrs=None)
|
||||||
|
|
||||||
|
Argument ``value`` is handled differently in this method from the
|
||||||
|
subclasses of :class:`~Widget` because it has to figure out how to
|
||||||
|
split a single value for display in multiple widgets.
|
||||||
|
|
||||||
|
The ``value`` argument used when rendering can be one of two things:
|
||||||
|
|
||||||
|
* A ``list``.
|
||||||
|
* A single value (e.g., a string) that is the "compressed" representation
|
||||||
|
of a ``list`` of values.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
In the second case -- i.e., if the value is *not* a list --
|
||||||
|
``render()`` will first decompress the value into a ``list`` before
|
||||||
|
rendering it. It does so by calling the ``decompress()`` method, which
|
||||||
|
:class:`MultiWidget`'s subclasses must implement (see above).
|
||||||
|
|
||||||
|
When ``render()`` executes its HTML rendering, each value in the list
|
||||||
|
is rendered with the corresponding widget -- the first value is
|
||||||
|
rendered in the first widget, the second value is rendered in the
|
||||||
|
second widget, etc.
|
||||||
|
|
||||||
|
Unlike in the single value widgets, method :meth:`~MultiWidget.render`
|
||||||
|
need not be implemented in the subclasses.
|
||||||
|
|
||||||
|
.. method:: format_output(rendered_widgets)
|
||||||
|
|
||||||
|
Given a list of rendered widgets (as strings), returns a Unicode string
|
||||||
|
representing the HTML for the whole lot.
|
||||||
|
|
||||||
|
This hook allows you to format the HTML design of the widgets any way
|
||||||
|
you'd like.
|
||||||
|
|
||||||
|
Here's an example widget which subclasses :class:`MultiWidget` to display
|
||||||
|
a date with the day, month, and year in different select boxes. This widget
|
||||||
|
is intended to be used with a :class:`~django.forms.DateField` rather than
|
||||||
|
a :class:`~django.forms.MultiValueField`, thus we have implemented
|
||||||
|
:meth:`~Widget.value_from_datadict`::
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
|
from django.forms import widgets
|
||||||
|
|
||||||
|
class DateSelectorWidget(widgets.MultiWidget):
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
# create choices for days, months, years
|
||||||
|
# example below, the rest snipped for brevity.
|
||||||
|
years = [(year, year) for year in (2011, 2012, 2013)]
|
||||||
|
_widgets = (
|
||||||
|
widgets.Select(attrs=attrs, choices=days),
|
||||||
|
widgets.Select(attrs=attrs, choices=months),
|
||||||
|
widgets.Select(attrs=attrs, choices=years),
|
||||||
|
)
|
||||||
|
super(DateSelectorWidget, self).__init__(_widgets, attrs)
|
||||||
|
|
||||||
|
def decompress(self, value):
|
||||||
|
if value:
|
||||||
|
return [value.day, value.month, value.year]
|
||||||
|
return [None, None, None]
|
||||||
|
|
||||||
|
def format_output(self, rendered_widgets):
|
||||||
|
return u''.join(rendered_widgets)
|
||||||
|
|
||||||
|
def value_from_datadict(self, data, files, name):
|
||||||
|
datelist = [
|
||||||
|
widget.value_from_datadict(data, files, name + '_%s' % i)
|
||||||
|
for i, widget in enumerate(self.widgets)]
|
||||||
|
try:
|
||||||
|
D = date(day=int(datelist[0]), month=int(datelist[1]),
|
||||||
|
year=int(datelist[2]))
|
||||||
|
except ValueError:
|
||||||
|
return ''
|
||||||
|
else:
|
||||||
|
return str(D)
|
||||||
|
|
||||||
|
The constructor creates several :class:`Select` widgets in a tuple. The
|
||||||
|
``super`` class uses this tuple to setup the widget.
|
||||||
|
|
||||||
|
The :meth:`~MultiWidget.format_output` method is fairly vanilla here (in
|
||||||
|
fact, it's the same as what's been implemented as the default for
|
||||||
|
``MultiWidget``), but the idea is that you could add custom HTML between
|
||||||
|
the widgets should you wish.
|
||||||
|
|
||||||
|
The required method :meth:`~MultiWidget.decompress` breaks up a
|
||||||
|
``datetime.date`` value into the day, month, and year values corresponding
|
||||||
|
to each widget. Note how the method handles the case where ``value`` is
|
||||||
|
``None``.
|
||||||
|
|
||||||
|
The default implementation of :meth:`~Widget.value_from_datadict` returns
|
||||||
|
a list of values corresponding to each ``Widget``. This is appropriate
|
||||||
|
when using a ``MultiWidget`` with a :class:`~django.forms.MultiValueField`,
|
||||||
|
but since we want to use this widget with a :class:`~django.forms.DateField`
|
||||||
|
which takes a single value, we have overridden this method to combine the
|
||||||
|
data of all the subwidgets into a ``datetime.date``. The method extracts
|
||||||
|
data from the ``POST`` dictionary and constructs and validates the date.
|
||||||
|
If it is valid, we return the string, otherwise, we return an empty string
|
||||||
|
which will cause ``form.is_valid`` to return ``False``.
|
||||||
|
|
||||||
.. _built-in widgets:
|
.. _built-in widgets:
|
||||||
|
|
||||||
|
@ -552,54 +666,6 @@ Composite widgets
|
||||||
:attr:`~Field.choices` attribute. If it does, it will override anything
|
:attr:`~Field.choices` attribute. If it does, it will override anything
|
||||||
you set here when the attribute is updated on the :class:`Field`.
|
you set here when the attribute is updated on the :class:`Field`.
|
||||||
|
|
||||||
``MultiWidget``
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. class:: MultiWidget
|
|
||||||
|
|
||||||
Wrapper around multiple other widgets. You'll probably want to use this
|
|
||||||
class with :class:`MultiValueField`.
|
|
||||||
|
|
||||||
Its ``render()`` method is different than other widgets', because it has to
|
|
||||||
figure out how to split a single value for display in multiple widgets.
|
|
||||||
|
|
||||||
Subclasses may implement ``format_output``, which takes the list of
|
|
||||||
rendered widgets and returns a string of HTML that formats them any way
|
|
||||||
you'd like.
|
|
||||||
|
|
||||||
The ``value`` argument used when rendering can be one of two things:
|
|
||||||
|
|
||||||
* A ``list``.
|
|
||||||
* A single value (e.g., a string) that is the "compressed" representation
|
|
||||||
of a ``list`` of values.
|
|
||||||
|
|
||||||
In the second case -- i.e., if the value is *not* a list -- ``render()``
|
|
||||||
will first decompress the value into a ``list`` before rendering it. It
|
|
||||||
does so by calling the ``decompress()`` method, which
|
|
||||||
:class:`MultiWidget`'s subclasses must implement. This method takes a
|
|
||||||
single "compressed" value and returns a ``list``. An example of this is how
|
|
||||||
:class:`SplitDateTimeWidget` turns a :class:`datetime` value into a list
|
|
||||||
with date and time split into two seperate values::
|
|
||||||
|
|
||||||
class SplitDateTimeWidget(MultiWidget):
|
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
def decompress(self, value):
|
|
||||||
if value:
|
|
||||||
return [value.date(), value.time().replace(microsecond=0)]
|
|
||||||
return [None, None]
|
|
||||||
|
|
||||||
When ``render()`` executes its HTML rendering, each value in the list is
|
|
||||||
rendered with the corresponding widget -- the first value is rendered in
|
|
||||||
the first widget, the second value is rendered in the second widget, etc.
|
|
||||||
|
|
||||||
:class:`MultiWidget` has one required argument:
|
|
||||||
|
|
||||||
.. attribute:: MultiWidget.widgets
|
|
||||||
|
|
||||||
An iterable containing the widgets needed.
|
|
||||||
|
|
||||||
``SplitDateTimeWidget``
|
``SplitDateTimeWidget``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue