From 662b372d022d0f638e62a3d4e3118f00a4fcce3d Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Thu, 16 Jun 2011 15:27:19 +0000 Subject: [PATCH] Fixed #16264 -- Improved form widget documentation. Many thanks to Bas Peschier. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16408 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/ref/forms/fields.txt | 1 + docs/ref/forms/widgets.txt | 358 ++++++++++++++++++++++++++----------- 2 files changed, 259 insertions(+), 100 deletions(-) diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt index 3fc5b8aabf..66c05e508e 100644 --- a/docs/ref/forms/fields.txt +++ b/docs/ref/forms/fields.txt @@ -278,6 +278,7 @@ as the rendered output. See the :ref:`format localization ` documentation for more information. +.. _built-in fields: Built-in ``Field`` classes -------------------------- diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt index dbdf109134..13c0c88b6b 100644 --- a/docs/ref/forms/widgets.txt +++ b/docs/ref/forms/widgets.txt @@ -11,9 +11,181 @@ 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. +Specifying widgets +------------------ + +Whenever you specify a field on a form, Django will use a default widget +that is appropriate to the type of data that is to be displayed. To find +which widget is used on which field, see the documentation about +:ref:`built-in fields`. + +However, if you want to use a different widget for a field, you can +just use the :attr:`~Field.widget` argument on the field definition. For +example: + + .. code-block:: python + + from django import forms + + class CommentForm(forms.Form): + name = forms.CharField() + url = forms.URLField() + comment = forms.CharField(widget=forms.Textarea) + +This would specify a form with a comment that uses a larger :class:`Textarea` +widget, rather than the default :class:`TextInput` widget. + + +Setting arguments for widgets +----------------------------- + +Many widgets have optional extra arguments; they can be set when defining the +widget on the field. In the following example, the +:attr:`~SelectDateWidget.years` attribute is set for a +:class:`~django.forms.widgets.extras.SelectDateWidget`: + + .. code-block:: python + + from django.forms.fields import DateField, ChoiceField, MultipleChoiceField + from django.forms.widgets import RadioSelect, CheckboxSelectMultiple + from django.forms.widgets.extras import SelectDateWidget + + BIRTH_YEAR_CHOICES = ('1980', '1981', '1982') + GENDER_CHOICES = (('m', 'Male'), ('f', 'Female')) + FAVOURITE_COLORS_CHOICES = (('blue', 'Blue'), + ('green', 'Green'), + ('black', 'Black')) + + class SimpleForm(forms.Form): + birth_year = DateField(widget=SelectDateWidget(years=YEAR_CHOICES)) + gender = ChoiceField(widget=RadioSelect, choices=RADIO_CHOICES) + favourite_colors = forms.MultipleChoiceField(required=False, + widget=CheckboxSelectMultiple, choices=CHECKBOX_CHOICES) + +See the :ref:`built-in widgets` for more information about which widgets +are available and which arguments they accept. + + +Widgets inheriting from the Select widget +----------------------------------------- + +Widgets inheriting from the :class:`Select` widget deal with choices. They +present the user with a list of options to choose from. The different widgets +present this choice differently; the :class:`Select` widget itself uses a +`` + Url: + Comment: + + +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. To do this, you use the +:attr:`Widget.attrs` argument when creating the widget: + +For example: + + .. code-block:: python + + class CommentForm(forms.Form): + name = forms.CharField( + widget=forms.TextInput(attrs={'class':'special'})) + url = forms.URLField() + comment = forms.CharField( + widget=forms.TextInput(attrs={'size':'40'})) + +Django will then include the extra attributes in the rendered output: + + .. code-block:: python + + >>> f = CommentForm(auto_id=False) + >>> f.as_table() + Name: + Url: + Comment: + +.. _built-in widgets: + +Built-in widgets +---------------- + Django provides a representation of all the basic HTML widgets, plus some commonly used groups of widgets: +.. class:: Widget + + This abstract class cannot be rendered, but provides the basic attribute :attr:`~Widget.attrs`. + + .. attribute:: Widget.attrs + + A dictionary containing HTML attributes to be set on the rendered widget. + + .. code-block:: python + + >>> name = forms.TextInput(attrs={'size': 10, 'title': 'Your name',}) + >>> name.render('name', 'A name') + u'' + + .. class:: TextInput Text input: ```` @@ -29,10 +201,10 @@ commonly used groups of widgets: Determines whether the widget will have a value filled in when the form is re-displayed after a validation error (default is ``False``). -.. versionchanged:: 1.3 - The default value for - :attr:`~PasswordInput.render_value` was - changed from ``True`` to ``False`` + .. versionchanged:: 1.3 + The default value for + :attr:`~PasswordInput.render_value` was + changed from ``True`` to ``False`` .. class:: HiddenInput @@ -42,6 +214,15 @@ commonly used groups of widgets: Multiple ```` 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`. + .. class:: FileInput File upload input: ```` @@ -64,7 +245,9 @@ commonly used groups of widgets: The format in which this field's initial value will be displayed. - If no ``format`` argument is provided, the default format is ``'%Y-%m-%d'``. + If no ``format`` argument is provided, the default format is the first + format found in :setting:`DATE_INPUT_FORMATS` and respects + :ref:`format-localization`. .. class:: DateTimeInput @@ -76,8 +259,9 @@ commonly used groups of widgets: The format in which this field's initial value will be displayed. - If no ``format`` argument is provided, the default format is ``'%Y-%m-%d - %H:%M:%S'``. + If no ``format`` argument is provided, the default format is the first + format found in :setting:`DATETIME_INPUT_FORMATS` and respects + :ref:`format-localization`. .. class:: TimeInput @@ -89,7 +273,9 @@ commonly used groups of widgets: The format in which this field's initial value will be displayed. - If no ``format`` argument is provided, the default format is ``'%H:%M:%S'``. + If no ``format`` argument is provided, the default format is the first + format found in :setting:`TIME_INPUT_FORMATS` and respects + :ref:`format-localization`. .. class:: Textarea @@ -103,15 +289,18 @@ commonly used groups of widgets: .. attribute:: CheckboxInput.check_test - A callable that takes the value of the CheckBoxInput - and returns ``True`` if the checkbox should be checked for - that value. + A callable that takes the value of the CheckBoxInput and returns + ``True`` if the checkbox should be checked for that value. .. class:: Select Select widget: ```` - Requires that your field provides :attr:`~Field.choices`. + .. attribute:: Select.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`. .. class:: NullBooleanSelect @@ -119,14 +308,12 @@ commonly used groups of widgets: .. class:: SelectMultiple - Select widget allowing multiple selection: ```` - - Requires that your field provides :attr:`~Field.choices`. + Similar to :class:`Select`, but allows multiple selection: + ```` .. class:: RadioSelect - A list of radio buttons: + Similar to :class:`Select`, but rendered as a list of radio buttons: .. code-block:: html @@ -135,11 +322,10 @@ commonly used groups of widgets: ... - Requires that your field provides :attr:`~Field.choices`. - .. class:: CheckboxSelectMultiple - A list of checkboxes: + Similar to :class:`SelectMultiple`, but rendered as a list of check + buttons: .. code-block:: html @@ -150,111 +336,83 @@ commonly used groups of widgets: .. class:: MultiWidget - Wrapper around multiple other widgets + Wrapper around multiple other widgets. You'll probably want to use this + class with :class:`MultiValueField`. -.. class:: SplitDateTimeWidget + 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. - Wrapper around two widgets: ``DateInput`` for the date, and ``TimeInput`` - for the time. + 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. - Takes two optional arguments, ``date_format`` and ``time_format``, which - work just like the ``format`` argument for ``DateInput`` and ``TimeInput``. + The ``value`` argument used when rendering can be one of two things: -.. currentmodule:: django.forms.extras.widgets + * A ``list``. + * A single value (e.g., a string) that is the "compressed" representation + of a ``list`` of values. -.. class:: SelectDateWidget - - Wrapper around three select widgets: one each for month, day, and year. - Note that this widget lives in a separate file from the standard widgets. - - Takes one optional argument: - - .. attribute:: List.years - - An optional list/tuple of years to use in the "year" select box. - The default is a list containing the current year and the next 9 years. + 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: .. code-block:: python - from django.forms.extras.widgets import SelectDateWidget + class SplitDateTimeWidget(MultiWidget): - date = forms.DateField(widget=SelectDateWidget()) + # ... -Specifying widgets ------------------- -.. currentmodule:: django.forms + def decompress(self, value): + if value: + return [value.date(), value.time().replace(microsecond=0)] + return [None, None] -.. attribute:: Form.widget + 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. -Whenever you specify a field on a form, Django will use a default widget -that is appropriate to the type of data that is to be displayed. To find -which widget is used on which field, see the documentation for the -built-in Field classes. + :class:`MultiWidget` has one required argument: -However, if you want to use a different widget for a field, you can - -just use the 'widget' argument on the field definition. For example:: + .. attribute:: MultiWidget.widgets - from django import forms + An iterable containing the widgets needed. - class CommentForm(forms.Form): - name = forms.CharField() - url = forms.URLField() - comment = forms.CharField(widget=forms.Textarea) +.. class:: SplitDateTimeWidget -This would specify a form with a comment that uses a larger Textarea widget, -rather than the default TextInput widget. + Wrapper (using :class:`MultiWidget`) around two widgets: :class:`DateInput` + for the date, and :class:`TimeInput` for the time. -Customizing widget instances ----------------------------- + ``SplitDateTimeWidget`` has two optional attributes: -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 'TextInput' widgets will appear the same -on your Web page. + .. attribute:: SplitDateTimeWidget.date_format -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. + Similar to :attr:`DateInput.format` -For example, take the following simple form:: + .. attribute:: SplitDateTimeWidget.time_format - class CommentForm(forms.Form): - name = forms.CharField() - url = forms.URLField() - comment = forms.CharField() - -This form will include three default TextInput widgets, with default rendering - -no CSS class, no extra attributes. This means that the input boxes provided for -each widget will be rendered exactly the same:: - - >>> f = CommentForm(auto_id=False) - >>> f.as_table() - Name: - Url: - Comment: + Similar to :attr:`TimeInput.format` -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. To do this, you use the ``attrs`` -argument when creating the widget: +.. class:: SplitHiddenDateTimeWidget -.. attribute:: Widget.attrs + Similar to :class:`SplitDateTimeWidget`, but uses :class:`HiddenInput` for + both date and time. -For example:: +.. currentmodule:: django.forms.widgets.extras - class CommentForm(forms.Form): - name = forms.CharField( - widget=forms.TextInput(attrs={'class':'special'})) - url = forms.URLField() - comment = forms.CharField( - widget=forms.TextInput(attrs={'size':'40'})) +.. class:: SelectDateWidget -Django will then include the extra attributes in the rendered output:: + Wrapper around three :class:`~django.forms.Select` widgets: one each for + month, day, and year. Note that this widget lives in a separate file from + the standard widgets. - >>> f = CommentForm(auto_id=False) - >>> f.as_table() - Name: - Url: - Comment: + Takes one optional argument: + + .. attribute:: SelectDateWidget.years + + An optional list/tuple of years to use in the "year" select box. + The default is a list containing the current year and the next 9 years.