Improved custom MultiWidget example in docs.

This commit is contained in:
Adam Johnson 2019-11-23 10:21:53 +00:00 committed by Mariusz Felisiak
parent abc51d44af
commit 7742cc0c8f
1 changed files with 32 additions and 38 deletions

View File

@ -410,57 +410,51 @@ foundation for custom widgets.
:meth:`~Widget.value_from_datadict`:: :meth:`~Widget.value_from_datadict`::
from datetime import date from datetime import date
from django.forms import widgets from django import forms
class DateSelectorWidget(widgets.MultiWidget): class DateSelectorWidget(forms.MultiWidget):
def __init__(self, attrs=None): def __init__(self, attrs=None):
# create choices for days, months, years days = [(day, day) for day in range(1, 32)]
# example below, the rest snipped for brevity. months = [(month, month) for month in range(1, 13)]
years = [(year, year) for year in (2011, 2012, 2013)] years = [(year, year) for year in [2018, 2019, 2020]]
_widgets = ( widgets = [
widgets.Select(attrs=attrs, choices=days), forms.Select(attrs=attrs, choices=days),
widgets.Select(attrs=attrs, choices=months), forms.Select(attrs=attrs, choices=months),
widgets.Select(attrs=attrs, choices=years), forms.Select(attrs=attrs, choices=years),
) ]
super().__init__(_widgets, attrs) super().__init__(widgets, attrs)
def decompress(self, value): def decompress(self, value):
if value: if isinstance(value, date):
return [value.day, value.month, value.year] return [value.day, value.month, value.year]
elif isinstance(value, str):
year, month, day = value.split('-')
return [day, month, year]
return [None, None, None] return [None, None, None]
def value_from_datadict(self, data, files, name): def value_from_datadict(self, data, files, name):
datelist = [ day, month, year = super().value_from_datadict(data, files, name)
widget.value_from_datadict(data, files, name + '_%s' % i) # DateField expects a single string that it can parse into a date.
for i, widget in enumerate(self.widgets)] return '{}-{}-{}'.format(year, month, day)
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 The constructor creates several :class:`Select` widgets in a list. The
``super`` class uses this tuple to setup the widget. ``super()`` method uses this list to setup the widget.
The required method :meth:`~MultiWidget.decompress` breaks up a The required method :meth:`~MultiWidget.decompress` breaks up a
``datetime.date`` value into the day, month, and year values corresponding ``datetime.date`` value into the day, month, and year values corresponding
to each widget. Note how the method handles the case where ``value`` is to each widget. If an invalid date was selected, such as the non-existent
``None``. 30th February, the :class:`~django.forms.DateField` passes this method a
string instead, so that needs parsing. The final ``return`` handles when
``value`` is ``None``, meaning we don't have any defaults for our
subwidgets.
The default implementation of :meth:`~Widget.value_from_datadict` returns The default implementation of :meth:`~Widget.value_from_datadict` returns a
a list of values corresponding to each ``Widget``. This is appropriate list of values corresponding to each ``Widget``. This is appropriate when
when using a ``MultiWidget`` with a :class:`~django.forms.MultiValueField`, using a ``MultiWidget`` with a :class:`~django.forms.MultiValueField`. But
but since we want to use this widget with a :class:`~django.forms.DateField` 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 which takes a single value, we have overridden this method. The
data of all the subwidgets into a ``datetime.date``. The method extracts implementation here combines the data from the subwidgets into a string in
data from the ``POST`` dictionary and constructs and validates the date. the format that :class:`~django.forms.DateField` expects.
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: