Ticket #13997: 13997.4.diff

File 13997.4.diff, 10.1 KB (added by Tim Graham, 11 years ago)
  • docs/ref/forms/widgets.txt

    diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt
    index 3c45893..5185a8e 100644
    a b foundation for custom widgets.  
    214214        The 'value' given is not guaranteed to be valid input, therefore
    215215        subclass implementations should program defensively.
    216216
     217    .. method:: value_from_datadict(self, data, files, name)
     218
     219        Given a dictionary of data and this widget's name, returns the value
     220        of this widget. Returns ``None`` if a value wasn't provided.
     221
    217222.. class:: MultiWidget(widgets, attrs=None)
    218223
    219224    A widget that is composed of multiple widgets.
    220225    :class:`~django.forms.widgets.MultiWidget` works hand in hand with the
    221226    :class:`~django.forms.MultiValueField`.
    222227
    223     .. method:: render(name, value, attrs=None)
     228    :class:`MultiWidget` has one required argument:
    224229
    225         Argument `value` is handled differently in this method from the
    226         subclasses of :class:`~Widget`.
     230    .. attribute:: MultiWidget.widgets
    227231
    228         If `value` is a list, output of :meth:`~MultiWidget.render` will be a
    229         concatenation of rendered child widgets. If `value` is not a list, it
    230         will be first processed by the method :meth:`~MultiWidget.decompress()`
    231         to create the list and then processed as above.
     232        An iterable containing the widgets needed.
    232233
    233         Unlike in the single value widgets, method :meth:`~MultiWidget.render`
    234         need not be implemented in the subclasses.
     234    And one required method:
    235235
    236236    .. method:: decompress(value)
    237237
    238         Returns a list of "decompressed" values for the given value of the
    239         multi-value field that makes use of the widget. The input value can be
    240         assumed as valid, but not necessarily non-empty.
     238        This method takes a single "compressed" value from the field and
     239        returns a list of "decompressed" values. The input value can be
     240        assumed valid, but not necessarily non-empty.
    241241
    242242        This method **must be implemented** by the subclass, and since the
    243243        value may be empty, the implementation must be defensive.
    244244
    245245        The rationale behind "decompression" is that it is necessary to "split"
    246         the combined value of the form field into the values of the individual
    247         field encapsulated within the multi-value field (e.g. when displaying
    248         the partially or fully filled-out form).
     246        the combined value of the form field into the values for each widget.
     247
     248        An example of this is how :class:`SplitDateTimeWidget` turns a
     249        :class:`datetime` value into a list with date and time split into two
     250        separate values::
     251
     252            class SplitDateTimeWidget(MultiWidget):
     253
     254                # ...
     255
     256                def decompress(self, value):
     257                    if value:
     258                        return [value.date(), value.time().replace(microsecond=0)]
     259                    return [None, None]
    249260
    250261        .. tip::
    251262
    foundation for custom widgets.  
    254265            with the opposite responsibility - to combine cleaned values of
    255266            all member fields into one.
    256267
     268    Other methods that may be useful to override include:
     269
     270    .. method:: render(name, value, attrs=None)
     271
     272        Argument ``value`` is handled differently in this method from the
     273        subclasses of :class:`~Widget` because it has to figure out how to
     274        split a single value for display in multiple widgets.
     275
     276        The ``value`` argument used when rendering can be one of two things:
     277
     278        * A ``list``.
     279        * A single value (e.g., a string) that is the "compressed" representation
     280          of a ``list`` of values.
     281
     282        If `value` is a list, output of :meth:`~MultiWidget.render` will be a
     283        concatenation of rendered child widgets. If `value` is not a list, it
     284        will be first processed by the method :meth:`~MultiWidget.decompress()`
     285        to create the list and then processed as above.
     286
     287        In the second case -- i.e., if the value is *not* a list --
     288        ``render()`` will first decompress the value into a ``list`` before
     289        rendering it. It does so by calling the ``decompress()`` method, which
     290        :class:`MultiWidget`'s subclasses must implement (see above).
     291
     292        When ``render()`` executes its HTML rendering, each value in the list
     293        is rendered with the corresponding widget -- the first value is
     294        rendered in the first widget, the second value is rendered in the
     295        second widget, etc.
     296
     297        Unlike in the single value widgets, method :meth:`~MultiWidget.render`
     298        need not be implemented in the subclasses.
     299
     300    .. method:: format_output(rendered_widgets)
     301
     302        Given a list of rendered widgets (as strings), returns a Unicode string
     303        representing the HTML for the whole lot.
     304
     305        This hook allows you to format the HTML design of the widgets any way
     306        you'd like.
     307
     308    Here's an example widget which subclasses :class:`MultiWidget` to display
     309    a date with the day, month, and year in different select boxes. This widget
     310    is intended to be used with a :class:`~django.forms.DateField` rather than
     311    a :class:`~django.forms.MultiValueField`, thus we have implemented
     312    :meth:`~Widget.value_from_datadict`::
     313
     314        from datetime import date
     315        from django.forms import widgets
     316
     317        class DateSelectorWidget(widgets.MultiWidget):
     318            def __init__(self, attrs=None):
     319                # create choices for days, months, years
     320                # example below, the rest snipped for brevity.
     321                years = [(year, year) for year in (2011, 2012, 2013)]
     322                _widgets = (
     323                    widgets.Select(attrs=attrs, choices=days),
     324                    widgets.Select(attrs=attrs, choices=months),
     325                    widgets.Select(attrs=attrs, choices=years),
     326                )
     327                super(DateSelectorWidget, self).__init__(_widgets, attrs)
     328
     329            def decompress(self, value):
     330                if value:
     331                    return [value.day, value.month, value.year]
     332                return [None, None, None]
     333
     334            def format_output(self, rendered_widgets):
     335                return u''.join(rendered_widgets)
     336
     337            def value_from_datadict(self, data, files, name):
     338                datelist = [
     339                    widget.value_from_datadict(data, files, name + '_%s' % i)
     340                    for i, widget in enumerate(self.widgets)]
     341                try:
     342                    D = date(day=int(datelist[0]), month=int(datelist[1]),
     343                            year=int(datelist[2]))
     344                except ValueError:
     345                    return ''
     346                else:
     347                    return str(D)
     348
     349    The constructor creates several :class:`Select` widgets in a tuple. The
     350    ``super`` class uses this tuple to setup the widget.
     351
     352    The :meth:`~MultiWidget.format_output` method is fairly vanilla here (in
     353    fact, it's the same as what's been implemented as the default for
     354    ``MultiWidget``), but the idea is that you could add custom HTML between
     355    the widgets should you wish.
     356
     357    The required method :meth:`~MultiWidget.decompress` breaks up a
     358    ``datetime.date`` value into the day, month, and year values corresponding
     359    to each widget. Note how the method handles the case where ``value`` is
     360    ``None``.
     361
     362    The default implementation of :meth:`~Widget.value_from_datadict` returns
     363    a list of values corresponding to each ``Widget``.  This is appropriate
     364    when using a ``MultiWidget`` with a :class:`~django.forms.MultiValueField`,
     365    but since we want to use this widget with a :class:`~django.forms.DateField`
     366    which takes a single value, we have overridden this method to combine the
     367    data of all the sub­widgets into a ``datetimem.date``. The method extracts
     368    data from the ``POST`` dictionary and constructs and validates the date.
     369    If it is valid, we return the string, otherwise, we return an empty string
     370    which will cause ``form.is_valid`` to return ``False``.
    257371
    258372.. _built-in widgets:
    259373
    Composite widgets  
    552666        :attr:`~Field.choices` attribute. If it does, it will override anything
    553667        you set here when the attribute is updated on the :class:`Field`.
    554668
    555 ``MultiWidget``
    556 ~~~~~~~~~~~~~~~
    557 
    558 .. class:: MultiWidget
    559 
    560     Wrapper around multiple other widgets. You'll probably want to use this
    561     class with :class:`MultiValueField`.
    562 
    563     Its ``render()`` method is different than other widgets', because it has to
    564     figure out how to split a single value for display in multiple widgets.
    565 
    566     Subclasses may implement ``format_output``, which takes the list of
    567     rendered widgets and returns a string of HTML that formats them any way
    568     you'd like.
    569 
    570     The ``value`` argument used when rendering can be one of two things:
    571 
    572     * A ``list``.
    573     * A single value (e.g., a string) that is the "compressed" representation
    574       of a ``list`` of values.
    575 
    576     In the second case -- i.e., if the value is *not* a list -- ``render()``
    577     will first decompress the value into a ``list`` before rendering it. It
    578     does so by calling the ``decompress()`` method, which
    579     :class:`MultiWidget`'s subclasses must implement. This method takes a
    580     single "compressed" value and returns a ``list``. An example of this is how
    581     :class:`SplitDateTimeWidget` turns a :class:`datetime` value into a list
    582     with date and time split into two seperate values::
    583 
    584         class SplitDateTimeWidget(MultiWidget):
    585 
    586             # ...
    587 
    588             def decompress(self, value):
    589                 if value:
    590                     return [value.date(), value.time().replace(microsecond=0)]
    591                 return [None, None]
    592 
    593     When ``render()`` executes its HTML rendering, each value in the list is
    594     rendered with the corresponding widget -- the first value is rendered in
    595     the first widget, the second value is rendered in the second widget, etc.
    596 
    597     :class:`MultiWidget` has one required argument:
    598 
    599     .. attribute:: MultiWidget.widgets
    600 
    601         An iterable containing the widgets needed.
    602 
    603669``SplitDateTimeWidget``
    604670~~~~~~~~~~~~~~~~~~~~~~~
    605671
Back to Top