Ticket #11185: widget-evgeny1.diff

File widget-evgeny1.diff, 18.8 KB (added by fadeev, 14 years ago)

finished reference for creation of custom widgets, improved cross-linking between form-fields, widgets, and form media pages, changed link on main page from Built-in widgets to Widgets

  • django/forms/widgets.py

    diff --git a/django/forms/widgets.py b/django/forms/widgets.py
    index dd5868f..701fa79 100644
    a b class MediaDefiningClass(type):  
    136136        return new_class
    137137
    138138class Widget(object):
     139    """Base class for all :ref:`built-in widgets <builtin-widgets>`
     140    """
    139141    __metaclass__ = MediaDefiningClass
    140142    is_hidden = False          # Determines whether this corresponds to an <input type="hidden">.
    141143    needs_multipart_form = False # Determines does this widget need multipart-encrypted form
    class Widget(object):  
    156158
    157159    def render(self, name, value, attrs=None):
    158160        """
    159         Returns this Widget rendered as HTML, as a Unicode string.
     161        Returns HTML for the widget, as a Unicode string.
    160162
    161         The 'value' given is not guaranteed to be valid input, so subclass
    162         implementations should program defensively.
     163        The 'value' given is not guaranteed to be valid input,
     164        therefore subclass implementations should program defensively.
    163165        """
    164166        raise NotImplementedError
    165167
  • docs/index.txt

    diff --git a/docs/index.txt b/docs/index.txt
    index 0cf066e..ee7ab15 100644
    a b Forms  
    124124      :doc:`Overview <topics/forms/index>` |
    125125      :doc:`Form API <ref/forms/api>` |
    126126      :doc:`Built-in fields <ref/forms/fields>` |
    127       :doc:`Built-in widgets <ref/forms/widgets>`
     127      :doc:`Widgets <ref/forms/widgets>`
    128128
    129129    * **Advanced:**
    130130      :doc:`Forms for models <topics/forms/modelforms>` |
  • docs/ref/forms/fields.txt

    diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt
    index b49864f..20ba4c7 100644
    a b as the rendered output.  
    278278See the :ref:`format localization <format-localization>` documentation for
    279279more information.
    280280
     281.. _builtin-form-fields:
    281282
    282283Built-in ``Field`` classes
    283284--------------------------
    Takes one extra required argument:  
    797798        ...
    798799        ValidationError: [u'Ensure this value has at most 20 characters (it has 28).']
    799800
     801.. _multi-value-field:
     802
    800803``MultiValueField``
    801804~~~~~~~~~~~~~~~~~~~
    802805
    803 .. class:: MultiValueField(**kwargs)
     806.. class:: MultiValueField(fields = (), **kwargs)
     807
     808    Aggregates the logic of multiple fields that together
     809    produce a single cleaned value.
     810
     811    This field is abstract and must be subclassed.
     812    Also, in contrast with the single-value fields
     813    the subclasses of :class:`MultiValueField`
     814    must not implement :meth:`~forms.Field.clean`
     815    but
     816    instead - implement :meth:`~forms.MultiValueField.compress`.
    804817
    805     * Default widget: ``TextInput``
    806818    * Empty value: ``''`` (an empty string)
    807819    * Normalizes to: the type returned by the ``compress`` method of the subclass.
    808820    * Validates that the given value against each of the fields specified
    809821      as an argument to the ``MultiValueField``.
    810822    * Error message keys: ``required``, ``invalid``
    811823
    812     This abstract field (must be subclassed) aggregates the logic of multiple
    813     fields. Subclasses should not have to implement clean(). Instead, they must
    814     implement compress(), which takes a list of valid values and returns a
    815     "compressed" version of those values -- a single value.  For example,
    816     :class:`SplitDateTimeField` is a subclass which combines a time field and
    817     a date field into a datetime object.
    818 
    819 Takes one extra required argument:
    820 
    821824.. attribute:: MultiValueField.fields
    822825
    823     A list of fields which are cleaned into a single field. Each value in
    824     ``clean`` is cleaned by the corresponding field in ``fields`` -- the first
    825     value is cleaned by the first field, the second value is cleaned by
    826     the second field, etc.  Once all fields are cleaned, the list of clean
    827     values is "compressed" into a single value.
     826    A tuple of fields whose values are cleaned
     827    and
     828    subsequently combined into a single value.
     829    More specifically, each value of the field
     830    is cleaned by the corresponding field in ``fields``
     831    -- the first value is cleaned by the first field,
     832    the second value is cleaned by the second field, etc.
     833    Once all fields are cleaned,
     834    the list of clean values is combined into a single value
     835    by the :meth:`~forms.MultiValueField.compress`.
     836
     837.. attribute:: MultiValueField.widget
     838
     839    Must be a subclass of
     840    :class:`django.forms.MultiWidget`.
     841    Default value is :class:`~forms.widgets.TextInput`,
     842    which is probably is not very useful in this case.
     843
     844.. method:: compress(data_list)
     845
     846    takes a list of valid values and returns
     847    a "compressed" version of those values -- in a single value.
     848    For example, :class:`SplitDateTimeField` is a subclass which combines a time field and
     849    a date field into a datetime object.
     850
     851    This method must be implemented in the subclasses.
    828852
    829853``SplitDateTimeField``
    830854~~~~~~~~~~~~~~~~~~~~~~
  • docs/ref/forms/widgets.txt

    diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt
    index dbdf109..7b452b1 100644
    a b  
    22Widgets
    33=======
    44
     5The term "widget" for the purposes of this document is narrower
     6than usually assumed elsewhere.
     7
     8In Django, widget is a Python object responsible for three things:
     9
     10#. generation of a subtree of HTML document
     11   where users can enter some data
     12#. extraction of raw data from the request's GET/POST dictionary
     13   entered into the element decribed above
     14#. optionally - provide media (e.g. javacript and CSS) for the widget
     15
     16Objects of widget classes wrap "native HTML widget" elements:
     17``input``,
     18``textarea``,
     19``select`` and ``option``,
     20but in addition they may include other HTML constructs
     21and
     22may contain more than one "native HTML widget".
     23
     24.. tip::
     25
     26    Widgets should not be confused with the :doc:`form fields </ref/forms/fields>`.
     27    Form fields concern with the logic of input validation,
     28    and are directly used in the templates.
     29    Widgets purely deal with
     30    rendering of the data entry for display on the web page
     31    and
     32    extraction of raw submitted data.
     33   
     34    However, in order to take effect, widgets do need to be
     35    :ref:`assigned <widget-to-field>`
     36    to the form fields.
     37
     38.. _builtin-widgets:
     39
     40Built-in widgets
     41----------------
     42
     43All built-in widgets are part of the ``django.forms.widgets`` module
     44and
     45handle many common use cases for
     46:ref:`the input of text <text-widgets>`,
     47:ref:`various checkboxes and selectors <selector-widgets>`,
     48:ref:`uploading files <file-upload-widgets>`,
     49and
     50:ref:`handling of multi-valued input <composite-widgets>`.
     51
    552.. module:: django.forms.widgets
    653   :synopsis: Django's built-in form widgets.
    754
    855.. currentmodule:: django.forms
    956
    10 A widget is Django's representation of a HTML input element. The widget
    11 handles the rendering of the HTML, and the extraction of data from a GET/POST
    12 dictionary that corresponds to the widget.
     57.. _text-widgets:
     58
     59Widgets handling input of text
     60^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    1361
    14 Django provides a representation of all the basic HTML widgets, plus some
    15 commonly used groups of widgets:
     62These widgets make use of the HTML elements `input` and `textarea`.
    1663
    1764.. class:: TextInput
    1865
    commonly used groups of widgets:  
    3077        form is re-displayed after a validation error (default is ``False``).
    3178
    3279.. versionchanged:: 1.3
    33     The default value for
    34     :attr:`~PasswordInput.render_value` was
     80    The default value for ``render_value`` was
    3581    changed from ``True`` to ``False``
    3682
    3783.. class:: HiddenInput
    3884
    3985    Hidden input: ``<input type='hidden' ...>``
    40 
    41 .. class:: MultipleHiddenInput
    42 
    43     Multiple ``<input type='hidden' ...>`` widgets.
    44 
    45 .. class:: FileInput
    46 
    47     File upload input: ``<input type='file' ...>``
    48 
    49 .. class:: ClearableFileInput
    50 
    51     .. versionadded:: 1.3
    52 
    53     File upload input: ``<input type='file' ...>``, with an additional checkbox
    54     input to clear the field's value, if the field is not required and has
    55     initial data.
     86   
     87    Please note that
     88    there also is a :ref:`MultipleHiddenInput <multiple-hidden>` widget
     89    that encapsulates a set of hidden input elements.
    5690
    5791.. class:: DateInput
    5892
    commonly used groups of widgets:  
    95129
    96130    Text area: ``<textarea>...</textarea>``
    97131
     132.. _selector-widgets:
     133
     134Selector and checkbox widgets
     135^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     136
    98137.. class:: CheckboxInput
    99138
    100139    Checkbox: ``<input type='checkbox' ...>``
    commonly used groups of widgets:  
    148187          ...
    149188        </ul>
    150189
    151 .. class:: MultiWidget
     190.. _file-upload-widgets:
     191
     192File upload widgets
     193^^^^^^^^^^^^^^^^^^^
     194
     195.. class:: FileInput
     196
     197    File upload input: ``<input type='file' ...>``
     198
     199.. class:: ClearableFileInput
     200
     201    .. versionadded:: 1.3
    152202
    153     Wrapper around multiple other widgets
     203    File upload input: ``<input type='file' ...>``, with an additional checkbox
     204    input to clear the field's value, if the field is not required and has
     205    initial data.
     206
     207
     208.. _composite-widgets:
     209
     210Composite widgets
     211^^^^^^^^^^^^^^^^^
    154212
    155213.. class:: SplitDateTimeWidget
    156214
    commonly used groups of widgets:  
    169227
    170228    Takes one optional argument:
    171229
    172     .. attribute:: List.years
     230    .. attribute:: SelectDateWidget.years
    173231
    174232        An optional list/tuple of years to use in the "year" select box.
    175233        The default is a list containing the current year and the next 9 years.
    commonly used groups of widgets:  
    180238
    181239        date = forms.DateField(widget=SelectDateWidget())
    182240
    183 Specifying widgets
    184 ------------------
    185 .. currentmodule:: django.forms
     241.. _multiple-hidden:
    186242
    187 .. attribute:: Form.widget
     243.. class:: MultipleHiddenInput
     244
     245    Multiple ``<input type='hidden' ...>`` widgets.
     246
     247.. _widget-to-field:
     248
     249Assigning widgets to form fields
     250--------------------------------
     251.. currentmodule:: django.forms
    188252
    189253Whenever you specify a field on a form, Django will use a default widget
    190254that is appropriate to the type of data that is to be displayed. To find
    191 which widget is used on which field, see the documentation for the
    192 built-in Field classes.
     255which widget is used on which field,
     256see the :ref:`documentation for the built-in Field classes
     257<builtin-form-fields>`.
    193258
    194259However, if you want to use a different widget for a field, you can -
    195 just use the 'widget' argument on the field definition. For example::
     260just use the ``widget`` argument on the field definition. For example::
    196261
    197262    from django import forms
    198263
    just use the 'widget' argument on the field definition. For example::  
    201266        url = forms.URLField()
    202267        comment = forms.CharField(widget=forms.Textarea)
    203268
    204 This would specify a form with a comment that uses a larger Textarea widget,
    205 rather than the default TextInput widget.
     269This will specify a form with a comment that uses a larger ``Textarea`` widget,
     270rather than the default ``TextInput`` widget.
     271
     272Styling and adding behavior to widgets
     273--------------------------------------
     274
     275When Django renders a widget as HTML,
     276it only produces the very minimal markup
     277- Django doesn't add class names
     278or
     279any other widget-specific attributes.
     280This means, for example, that
     281all instances of ``TextInput`` widget
     282will appear the same on your Web pages.
     283
     284There are two ways to customize widgets:
     285:ref:`per widget instance <styling-widget-instances>`
     286and
     287:ref:`per widget class <styling-widget-classes>`.
    206288
    207 Customizing widget instances
    208 ----------------------------
     289.. _styling-widget-instances:
    209290
    210 When Django renders a widget as HTML, it only renders the bare minimum
    211 HTML - Django doesn't add a class definition, or any other widget-specific
    212 attributes. This means that all 'TextInput' widgets will appear the same
    213 on your Web page.
     291Styling widget instances
     292^^^^^^^^^^^^^^^^^^^^^^^^
    214293
    215 If you want to make one widget look different to another, you need to
    216 specify additional attributes for each widget. When you specify a
    217 widget, you can provide a list of attributes that will be added to the
    218 rendered HTML for the widget.
     294If you want to make one widget instance look different from another,
     295all you will need to do is
     296specify additional attributes
     297- at the time when the widget object is instantiated
     298and assigned to a form field
     299(and perhaps add some rules to your .css files).
    219300
    220301For example, take the following simple form::
    221302
    each widget will be rendered exactly the same::  
    234315    <tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
    235316    <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
    236317
    237 
    238318On a real Web page, you probably don't want every widget to look the same. You
    239319might want a larger input element for the comment, and you might want the 'name'
    240320widget to have some special CSS class. To do this, you use the ``attrs``
    241 argument when creating the widget:
    242 
    243 .. attribute:: Widget.attrs
    244 
    245 For example::
     321argument when creating the widget::
    246322
    247323    class CommentForm(forms.Form):
    248324        name = forms.CharField(
    Django will then include the extra attributes in the rendered output::  
    258334    <tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr>
    259335    <tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
    260336    <tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>
     337
     338.. _styling-widget-classes:
     339
     340Styling widget classes
     341^^^^^^^^^^^^^^^^^^^^^^
     342
     343With widgets, it is possible to add media (``css`` and ``javascript``)
     344and more deeply customise their appearance and behavior.
     345
     346In a nutshell, you will need to subclass the widget
     347and either
     348:ref:`define a class "Media" <media-as-a-static-definition>`
     349as a member of the subclass,
     350or
     351:ref:`create a property "media" <dynamic-property>`,
     352returning an instance of that class.
     353
     354These methods involve somewhat advanced Python programming and are
     355described in detail in the
     356:doc:`Form Media </topics/forms/media>` tutorial.
     357
     358.. _base-widget-classes:
     359
     360Base Widget classes
     361-------------------
     362
     363Base widget classes
     364:class:`~django.forms.widgets.Widget`
     365and
     366:class:`~django.forms.widgets.MultiWidget`
     367are subclassed by
     368all the :ref:`built-in widgets <builtin-widgets>`
     369and may serve as a foundation for the custom ones
     370(but, please see the warning below).
     371
     372.. warning::
     373
     374   Interfaces of classes
     375   :class:`~django.forms.widgets.Widget`
     376   and
     377   :class:`~django.forms.widgets.MultiWidget`
     378   are still in flux,
     379   therefore -
     380   please take care to test all of your custom widgets
     381   when upgrading django.
     382   It is a particularly good idea to create
     383   :doc:`automated test suite </topics/testing>`
     384   for any of your custom widgets.
     385
     386.. currentmodule:: django.forms
     387
     388.. class:: Widget(attrs=None)
     389
     390    .. method:: render(name, value, attrs=None)
     391
     392       Returns HTML for the widget, as a Unicode string.
     393       This method must be implemented by the subclass,
     394       otherwise ``NotImplementedError`` will be raised.
     395
     396       The 'value' given is not guaranteed to be valid input,
     397       therefore subclass implementations should program defensively.
     398
     399.. class:: MultiWidget(widgets, attrs=None)
     400
     401    A widget that is composed of multiple widgets.
     402    :class:`~django.forms.widgets.MultiWidget` works hand in hand
     403    with the :class:`~django.forms.MultiValueField`.
     404
     405    .. method:: render(name, value, attrs=None)
     406
     407        Argument `value` is handled differently in this method
     408        from the subclasses of :meth:`~django.forms.widgets.Widget`.
     409
     410        If `value` is a list, output of
     411        :meth:`~django.forms.widgets.MultiWidget.render`
     412        will be a concatenation of rendered child widgets.
     413        If `value` is not a list, it will be first processed
     414        by the method :meth:`~django.forms.widgets.MultiWidget.decompress()`
     415        to create the list, and then processed as above.
     416
     417        Unlike in the single value widgets,
     418        method :meth:`~django.forms.widgets.MultiWidget.render`
     419        should not be implemented in the subclasses
     420        of :class:`~django.forms.widgets.MultiWidget`.
     421
     422    .. method:: decompress(value)
     423
     424        Returns a list of "decompressed" values
     425        for the given value of the multi-value field
     426        that makes use of the widget.
     427        The input value can be assumed as valid,
     428        but not necessarily non-empty.
     429
     430        This method **must be implemented** by the subclass,
     431        and since the value may be empty,
     432        the implementation must be defensive.
     433
     434        The rationale behind "decompression" is that
     435        it is necessary to "split" the combined value
     436        of the form field into the values of the individual fields
     437        encapsulated within the multi-value field
     438        (e.g. when displaying the partially or fully filled-out form).
     439
     440        .. tip::
     441
     442            Please, note that :class:`~django.forms.MultiValueField` has
     443            a complementary method :meth:`~django.forms.MultiValueField.compress`
     444            with the opposite responsibility - combine cleaned values of
     445            all memeber fields into one.
     446
     447.. custom-widgets:
     448
     449Custom Widgets
     450--------------
     451
     452Those who wish to create their own custom widgets
     453can use the APIs of classes
     454:class:`~django.forms.widgets.Widget`
     455and
     456:class:`~django.forms.widgets.MultiWidget`.
     457
     458However, please do notice that those APIs
     459are subject to the future changes.
     460
     461Since there are two :ref:`widget base classes <base-widget-classes>`,
     462two types of custom widgets are possible:
     463single-value widgets
     464and
     465multi-value composite widgets.
     466The patterns of their creation are somewhat different.
  • docs/topics/forms/media.txt

    diff --git a/docs/topics/forms/media.txt b/docs/topics/forms/media.txt
    index 0eb3e91..2f6b3ec 100644
    a b in a form suitable for easy inclusion on your Web page.  
    3838    whichever toolkit suits your requirements. Django is able to integrate
    3939    with any JavaScript toolkit.
    4040
     41.. _media-as-a-static-definition:
     42
    4143Media as a static definition
    4244----------------------------
    4345
    declaration to the media declaration::  
    164166    <script type="text/javascript" src="http://media.example.com/whizbang.js"></script>
    165167
    166168If you require even more control over media inheritance, define your media
    167 using a `dynamic property`_. Dynamic properties give you complete control over
     169using a :ref:`dynamic property <dynamic-property>` technique.
     170Dynamic properties give you complete control over
    168171which media files are inherited, and which are not.
    169172
    170 .. _dynamic property: `Media as a dynamic property`_
     173.. _dynamic-property:
    171174
    172175Media as a dynamic property
    173176---------------------------
    Paths in media definitions  
    198201.. versionchanged:: 1.3
    199202
    200203Paths used to specify media can be either relative or absolute. If a path
    201 starts with '/', 'http://' or 'https://', it will be interpreted as an absolute
     204starts with ``/``, ``http://`` or ``https://``, it will be interpreted as an absolute
    202205path, and left as-is. All other paths will be prepended with the value of
    203206the appropriate prefix.
    204207
Back to Top