Ticket #3023: newforms.patch

File newforms.patch, 38.5 KB (added by Chris Beaven, 18 years ago)
  • django/newforms/forms.py

     
    33"""
    44
    55from fields import Field
    6 from widgets import TextInput, Textarea
     6from widgets import TextInput, Textarea, Label
    77from util import ErrorDict, ErrorList, ValidationError
    88
    99NON_FIELD_ERRORS = '__all__'
     
    6464
    6565    def as_table(self):
    6666        "Returns this form rendered as an HTML <table>."
    67         output = u'\n'.join(['<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
     67        output = u'\n'.join(['<tr><td>%s:</td><td>%s</td></tr>' % BoundField(self, field, name).with_label() for name, field in self.fields.items()])
    6868        return '<table>\n%s\n</table>' % output
    6969
    7070    def as_ul(self):
    7171        "Returns this form rendered as an HTML <ul>."
    72         output = u'\n'.join(['<li>%s: %s</li>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
     72        output = u'\n'.join(['<li>%s: %s</li>' % BoundField(self, field, name).with_label() for name, field in self.fields.items()])
    7373        return '<ul>\n%s\n</ul>' % output
    7474
    7575    def as_table_with_errors(self):
     
    8282            bf = BoundField(self, field, name)
    8383            if bf.errors:
    8484                output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors]))
    85             output.append('<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), bf))
     85            output.append('<tr><td>%s:</td><td>%s</td></tr>' % bf.with_label())
    8686        return '<table>\n%s\n</table>' % '\n'.join(output)
    8787
    8888    def as_ul_with_errors(self):
     
    9696            line = '<li>'
    9797            if bf.errors:
    9898                line += '<ul>%s</ul>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors])
    99             line += '%s: %s</li>' % (pretty_name(name), bf)
     99            line += '%s: %s</li>' % bf.with_label()
    100100            output.append(line)
    101101        return '<ul>\n%s\n</ul>' % '\n'.join(output)
    102102
     
    167167    def as_textarea(self, attrs=None):
    168168        "Returns a string of HTML for representing this as a <textarea>."
    169169        return self.as_widget(Textarea(), attrs)
     170   
     171    def with_label(self, widget=None, attrs=None):
     172        """
     173        Returns a tuple of a label for this field and this field as an HTML
     174        widget.
     175        """
     176        if not widget:
     177            widget = self._field.widget
     178        widget = self.as_widget(widget)
     179        label = Label().render(self._name, pretty_name(self._name))
     180        return label, widget
     181 No newline at end of file
  • django/newforms/widgets.py

     
    66    'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
    77    'Textarea', 'CheckboxInput',
    88    'Select', 'SelectMultiple',
     9    'Label',
    910)
    1011
    1112from django.utils.html import escape
    1213from itertools import chain
    1314
     15# 'id' HTML attribute format (where %s is the widget name).
     16ID_FORMAT = 'id_%s'
     17
    1418try:
    1519    set # Only available in Python 2.4+
    1620except NameError:
    1721    from sets import Set as set # Python 2.3 fallback
    1822
    19 # Converts a dictionary to a single string with key="value", XML-style.
    20 # Assumes keys do not need to be XML-escaped.
    21 flatatt = lambda attrs: ' '.join(['%s="%s"' % (k, escape(v)) for k, v in attrs.items()])
     23# Converts a dictionary to a single string with key="value", XML-style with
     24# leading space. Assumes keys do not need to be XML-escaped.
     25flatatt = lambda attrs: ''.join([' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
    2226
    2327class Widget(object):
    2428    requires_data_list = False # Determines whether render()'s 'value' argument should be a list.
     
    2731
    2832    def render(self, name, value):
    2933        raise NotImplementedError
     34   
     35    def build_attrs(self, extra_attrs=None, **kwargs):
     36        attrs = dict(self.attrs)
     37        attrs.update(kwargs)
     38        if extra_attrs:
     39            attrs.update(extra_attrs)
     40        if attrs.has_key('name') and not attrs.has_key('id'):
     41            attrs['id'] = ID_FORMAT % attrs['name']
     42        return attrs
    3043
    3144class Input(Widget):
    3245    "Base class for all <input> widgets (except type='checkbox', which is special)"
    3346    input_type = None # Subclasses must define this.
    3447    def render(self, name, value, attrs=None):
    3548        if value is None: value = ''
    36         final_attrs = dict(self.attrs, type=self.input_type, name=name)
    37         if attrs:
    38             final_attrs.update(attrs)
     49        final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
    3950        if value != '': final_attrs['value'] = value # Only add the 'value' attribute if a value is non-empty.
    40         return u'<input %s />' % flatatt(final_attrs)
     51        return u'<input%s />' % flatatt(final_attrs)
    4152
    4253class TextInput(Input):
    4354    input_type = 'text'
     
    5465class Textarea(Widget):
    5566    def render(self, name, value, attrs=None):
    5667        if value is None: value = ''
    57         final_attrs = dict(self.attrs, name=name)
    58         if attrs:
    59             final_attrs.update(attrs)
    60         return u'<textarea %s>%s</textarea>' % (flatatt(final_attrs), escape(value))
     68        final_attrs = self.build_attrs(attrs, name=name)
     69        return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))
    6170
    6271class CheckboxInput(Widget):
    6372    def render(self, name, value, attrs=None):
    64         final_attrs = dict(self.attrs, type='checkbox', name=name)
    65         if attrs:
    66             final_attrs.update(attrs)
     73        final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
    6774        if value: final_attrs['checked'] = 'checked'
    68         return u'<input %s />' % flatatt(final_attrs)
     75        return u'<input%s />' % flatatt(final_attrs)
    6976
    7077class Select(Widget):
    7178    def __init__(self, attrs=None, choices=()):
     
    7582
    7683    def render(self, name, value, attrs=None, choices=()):
    7784        if value is None: value = ''
    78         final_attrs = dict(self.attrs, name=name)
    79         if attrs:
    80             final_attrs.update(attrs)
    81         output = [u'<select %s>' % flatatt(final_attrs)]
     85        final_attrs = self.build_attrs(attrs, name=name)
     86        output = [u'<select%s>' % flatatt(final_attrs)]
    8287        str_value = str(value) # Normalize to string.
    8388        for option_value, option_label in chain(self.choices, choices):
    8489            selected_html = (str(option_value) == str_value) and ' selected="selected"' or ''
     
    8994class SelectMultiple(Widget):
    9095    requires_data_list = True
    9196    def __init__(self, attrs=None, choices=()):
     97        super(SelectMultiple, self).__init__(attrs)
    9298        # choices can be any iterable
    93         self.attrs = attrs or {}
    9499        self.choices = choices
    95100
    96101    def render(self, name, value, attrs=None, choices=()):
    97102        if value is None: value = []
    98         final_attrs = dict(self.attrs, name=name)
    99         if attrs:
    100             final_attrs.update(attrs)
    101         output = [u'<select multiple="multiple" %s>' % flatatt(final_attrs)]
     103        final_attrs = self.build_attrs(attrs, name=name)
     104        output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)]
    102105        str_values = set([str(v) for v in value]) # Normalize to strings.
    103106        for option_value, option_label in chain(self.choices, choices):
    104107            selected_html = (str(option_value) in str_values) and ' selected="selected"' or ''
     
    111114
    112115class CheckboxSelectMultiple(Widget):
    113116    pass
     117
     118class Label(Widget):
     119    def render(self, name, value, attrs=None):
     120        if value is None: value = ''
     121        if name:
     122            # Only add the 'for' attribute if the widget name is not empty.
     123            kwargs = {'for': ID_FORMAT % name}
     124        else:
     125            kwargs = {}
     126        final_attrs = self.build_attrs(attrs, **kwargs)
     127        return u'<label%s>%s</label>' % (flatatt(final_attrs), escape(value))
  • tests/regressiontests/forms/tests.py

     
    77
    88>>> w = TextInput()
    99>>> w.render('email', '')
    10 u'<input type="text" name="email" />'
     10u'<input type="text" name="email" id="id_email" />'
    1111>>> w.render('email', None)
    12 u'<input type="text" name="email" />'
     12u'<input type="text" name="email" id="id_email" />'
    1313>>> w.render('email', 'test@example.com')
    14 u'<input type="text" name="email" value="test@example.com" />'
     14u'<input type="text" name="email" value="test@example.com" id="id_email" />'
    1515>>> w.render('email', 'some "quoted" & ampersanded value')
    16 u'<input type="text" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
     16u'<input type="text" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" id="id_email" />'
    1717>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
    18 u'<input type="text" name="email" value="test@example.com" class="fun" />'
     18u'<input id="id_email" type="text" name="email" value="test@example.com" class="fun" />'
    1919
    2020You can also pass 'attrs' to the constructor:
    2121>>> w = TextInput(attrs={'class': 'fun'})
    2222>>> w.render('email', '')
    23 u'<input type="text" class="fun" name="email" />'
     23u'<input id="id_email" type="text" class="fun" name="email" />'
    2424>>> w.render('email', 'foo@example.com')
    25 u'<input type="text" class="fun" value="foo@example.com" name="email" />'
     25u'<input id="id_email" type="text" class="fun" value="foo@example.com" name="email" />'
    2626
    2727'attrs' passed to render() get precedence over those passed to the constructor:
    2828>>> w = TextInput(attrs={'class': 'pretty'})
    2929>>> w.render('email', '', attrs={'class': 'special'})
    30 u'<input type="text" class="special" name="email" />'
     30u'<input id="id_email" type="text" class="special" name="email" />'
    3131
    3232# PasswordInput Widget ############################################################
    3333
    3434>>> w = PasswordInput()
    3535>>> w.render('email', '')
    36 u'<input type="password" name="email" />'
     36u'<input type="password" name="email" id="id_email" />'
    3737>>> w.render('email', None)
    38 u'<input type="password" name="email" />'
     38u'<input type="password" name="email" id="id_email" />'
    3939>>> w.render('email', 'test@example.com')
    40 u'<input type="password" name="email" value="test@example.com" />'
     40u'<input type="password" name="email" value="test@example.com" id="id_email" />'
    4141>>> w.render('email', 'some "quoted" & ampersanded value')
    42 u'<input type="password" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
     42u'<input type="password" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" id="id_email" />'
    4343>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
    44 u'<input type="password" name="email" value="test@example.com" class="fun" />'
     44u'<input id="id_email" type="password" name="email" value="test@example.com" class="fun" />'
    4545
    4646You can also pass 'attrs' to the constructor:
    4747>>> w = PasswordInput(attrs={'class': 'fun'})
    4848>>> w.render('email', '')
    49 u'<input type="password" class="fun" name="email" />'
     49u'<input id="id_email" type="password" class="fun" name="email" />'
    5050>>> w.render('email', 'foo@example.com')
    51 u'<input type="password" class="fun" value="foo@example.com" name="email" />'
     51u'<input id="id_email" type="password" class="fun" value="foo@example.com" name="email" />'
    5252
    5353'attrs' passed to render() get precedence over those passed to the constructor:
    5454>>> w = PasswordInput(attrs={'class': 'pretty'})
    5555>>> w.render('email', '', attrs={'class': 'special'})
    56 u'<input type="password" class="special" name="email" />'
     56u'<input id="id_email" type="password" class="special" name="email" />'
    5757
    5858# HiddenInput Widget ############################################################
    5959
    6060>>> w = HiddenInput()
    6161>>> w.render('email', '')
    62 u'<input type="hidden" name="email" />'
     62u'<input type="hidden" name="email" id="id_email" />'
    6363>>> w.render('email', None)
    64 u'<input type="hidden" name="email" />'
     64u'<input type="hidden" name="email" id="id_email" />'
    6565>>> w.render('email', 'test@example.com')
    66 u'<input type="hidden" name="email" value="test@example.com" />'
     66u'<input type="hidden" name="email" value="test@example.com" id="id_email" />'
    6767>>> w.render('email', 'some "quoted" & ampersanded value')
    68 u'<input type="hidden" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
     68u'<input type="hidden" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" id="id_email" />'
    6969>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
    70 u'<input type="hidden" name="email" value="test@example.com" class="fun" />'
     70u'<input id="id_email" type="hidden" name="email" value="test@example.com" class="fun" />'
    7171
    7272You can also pass 'attrs' to the constructor:
    7373>>> w = HiddenInput(attrs={'class': 'fun'})
    7474>>> w.render('email', '')
    75 u'<input type="hidden" class="fun" name="email" />'
     75u'<input id="id_email" type="hidden" class="fun" name="email" />'
    7676>>> w.render('email', 'foo@example.com')
    77 u'<input type="hidden" class="fun" value="foo@example.com" name="email" />'
     77u'<input id="id_email" type="hidden" class="fun" value="foo@example.com" name="email" />'
    7878
    7979'attrs' passed to render() get precedence over those passed to the constructor:
    8080>>> w = HiddenInput(attrs={'class': 'pretty'})
    8181>>> w.render('email', '', attrs={'class': 'special'})
    82 u'<input type="hidden" class="special" name="email" />'
     82u'<input id="id_email" type="hidden" class="special" name="email" />'
    8383
    8484# FileInput Widget ############################################################
    8585
    8686>>> w = FileInput()
    8787>>> w.render('email', '')
    88 u'<input type="file" name="email" />'
     88u'<input type="file" name="email" id="id_email" />'
    8989>>> w.render('email', None)
    90 u'<input type="file" name="email" />'
     90u'<input type="file" name="email" id="id_email" />'
    9191>>> w.render('email', 'test@example.com')
    92 u'<input type="file" name="email" value="test@example.com" />'
     92u'<input type="file" name="email" value="test@example.com" id="id_email" />'
    9393>>> w.render('email', 'some "quoted" & ampersanded value')
    94 u'<input type="file" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
     94u'<input type="file" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" id="id_email" />'
    9595>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
    96 u'<input type="file" name="email" value="test@example.com" class="fun" />'
     96u'<input id="id_email" type="file" name="email" value="test@example.com" class="fun" />'
    9797
    9898You can also pass 'attrs' to the constructor:
    9999>>> w = FileInput(attrs={'class': 'fun'})
    100100>>> w.render('email', '')
    101 u'<input type="file" class="fun" name="email" />'
     101u'<input id="id_email" type="file" class="fun" name="email" />'
    102102>>> w.render('email', 'foo@example.com')
    103 u'<input type="file" class="fun" value="foo@example.com" name="email" />'
     103u'<input id="id_email" type="file" class="fun" value="foo@example.com" name="email" />'
    104104
    105105'attrs' passed to render() get precedence over those passed to the constructor:
    106106>>> w = HiddenInput(attrs={'class': 'pretty'})
    107107>>> w.render('email', '', attrs={'class': 'special'})
    108 u'<input type="hidden" class="special" name="email" />'
     108u'<input id="id_email" type="hidden" class="special" name="email" />'
    109109
    110110# Textarea Widget #############################################################
    111111
    112112>>> w = Textarea()
    113113>>> w.render('msg', '')
    114 u'<textarea name="msg"></textarea>'
     114u'<textarea name="msg" id="id_msg"></textarea>'
    115115>>> w.render('msg', None)
    116 u'<textarea name="msg"></textarea>'
     116u'<textarea name="msg" id="id_msg"></textarea>'
    117117>>> w.render('msg', 'value')
    118 u'<textarea name="msg">value</textarea>'
     118u'<textarea name="msg" id="id_msg">value</textarea>'
    119119>>> w.render('msg', 'some "quoted" & ampersanded value')
    120 u'<textarea name="msg">some &quot;quoted&quot; &amp; ampersanded value</textarea>'
     120u'<textarea name="msg" id="id_msg">some &quot;quoted&quot; &amp; ampersanded value</textarea>'
    121121>>> w.render('msg', 'value', attrs={'class': 'pretty'})
    122 u'<textarea name="msg" class="pretty">value</textarea>'
     122u'<textarea id="id_msg" name="msg" class="pretty">value</textarea>'
    123123
    124124You can also pass 'attrs' to the constructor:
    125125>>> w = Textarea(attrs={'class': 'pretty'})
    126126>>> w.render('msg', '')
    127 u'<textarea class="pretty" name="msg"></textarea>'
     127u'<textarea id="id_msg" class="pretty" name="msg"></textarea>'
    128128>>> w.render('msg', 'example')
    129 u'<textarea class="pretty" name="msg">example</textarea>'
     129u'<textarea id="id_msg" class="pretty" name="msg">example</textarea>'
    130130
    131131'attrs' passed to render() get precedence over those passed to the constructor:
    132132>>> w = Textarea(attrs={'class': 'pretty'})
    133133>>> w.render('msg', '', attrs={'class': 'special'})
    134 u'<textarea class="special" name="msg"></textarea>'
     134u'<textarea id="id_msg" class="special" name="msg"></textarea>'
    135135
    136136# CheckboxInput Widget ########################################################
    137137
    138138>>> w = CheckboxInput()
    139139>>> w.render('is_cool', '')
    140 u'<input type="checkbox" name="is_cool" />'
     140u'<input type="checkbox" name="is_cool" id="id_is_cool" />'
    141141>>> w.render('is_cool', False)
    142 u'<input type="checkbox" name="is_cool" />'
     142u'<input type="checkbox" name="is_cool" id="id_is_cool" />'
    143143>>> w.render('is_cool', True)
    144 u'<input checked="checked" type="checkbox" name="is_cool" />'
     144u'<input checked="checked" type="checkbox" name="is_cool" id="id_is_cool" />'
    145145>>> w.render('is_cool', False, attrs={'class': 'pretty'})
    146 u'<input type="checkbox" name="is_cool" class="pretty" />'
     146u'<input id="id_is_cool" type="checkbox" name="is_cool" class="pretty" />'
    147147
    148148You can also pass 'attrs' to the constructor:
    149149>>> w = CheckboxInput(attrs={'class': 'pretty'})
    150150>>> w.render('is_cool', '')
    151 u'<input type="checkbox" class="pretty" name="is_cool" />'
     151u'<input id="id_is_cool" type="checkbox" class="pretty" name="is_cool" />'
    152152
    153153'attrs' passed to render() get precedence over those passed to the constructor:
    154154>>> w = CheckboxInput(attrs={'class': 'pretty'})
    155155>>> w.render('is_cool', '', attrs={'class': 'special'})
    156 u'<input type="checkbox" class="special" name="is_cool" />'
     156u'<input id="id_is_cool" type="checkbox" class="special" name="is_cool" />'
    157157
    158158# Select Widget ###############################################################
    159159
    160160>>> w = Select()
    161161>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
    162 <select name="beatle">
     162<select name="beatle" id="id_beatle">
    163163<option value="J" selected="selected">John</option>
    164164<option value="P">Paul</option>
    165165<option value="G">George</option>
     
    168168
    169169If the value is None, none of the options are selected:
    170170>>> print w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
    171 <select name="beatle">
     171<select name="beatle" id="id_beatle">
    172172<option value="J">John</option>
    173173<option value="P">Paul</option>
    174174<option value="G">George</option>
     
    177177
    178178If the value corresponds to a label (but not to an option value), none of the options are selected:
    179179>>> print w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
    180 <select name="beatle">
     180<select name="beatle" id="id_beatle">
    181181<option value="J">John</option>
    182182<option value="P">Paul</option>
    183183<option value="G">George</option>
     
    186186
    187187The value is compared to its str():
    188188>>> print w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')])
    189 <select name="num">
     189<select name="num" id="id_num">
    190190<option value="1">1</option>
    191191<option value="2" selected="selected">2</option>
    192192<option value="3">3</option>
    193193</select>
    194194>>> print w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)])
    195 <select name="num">
     195<select name="num" id="id_num">
    196196<option value="1">1</option>
    197197<option value="2" selected="selected">2</option>
    198198<option value="3">3</option>
    199199</select>
    200200>>> print w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)])
    201 <select name="num">
     201<select name="num" id="id_num">
    202202<option value="1">1</option>
    203203<option value="2" selected="selected">2</option>
    204204<option value="3">3</option>
     
    209209...     for i in range(5):
    210210...         yield (i, i)
    211211>>> print w.render('num', 2, choices=get_choices())
    212 <select name="num">
     212<select name="num" id="id_num">
    213213<option value="0">0</option>
    214214<option value="1">1</option>
    215215<option value="2" selected="selected">2</option>
     
    220220You can also pass 'choices' to the constructor:
    221221>>> w = Select(choices=[(1, 1), (2, 2), (3, 3)])
    222222>>> print w.render('num', 2)
    223 <select name="num">
     223<select name="num" id="id_num">
    224224<option value="1">1</option>
    225225<option value="2" selected="selected">2</option>
    226226<option value="3">3</option>
     
    228228
    229229If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
    230230>>> print w.render('num', 2, choices=[(4, 4), (5, 5)])
    231 <select name="num">
     231<select name="num" id="id_num">
    232232<option value="1">1</option>
    233233<option value="2" selected="selected">2</option>
    234234<option value="3">3</option>
     
    240240
    241241>>> w = SelectMultiple()
    242242>>> print w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
    243 <select multiple="multiple" name="beatles">
     243<select multiple="multiple" name="beatles" id="id_beatles">
    244244<option value="J" selected="selected">John</option>
    245245<option value="P">Paul</option>
    246246<option value="G">George</option>
    247247<option value="R">Ringo</option>
    248248</select>
    249249>>> print w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
    250 <select multiple="multiple" name="beatles">
     250<select multiple="multiple" name="beatles" id="id_beatles">
    251251<option value="J" selected="selected">John</option>
    252252<option value="P" selected="selected">Paul</option>
    253253<option value="G">George</option>
    254254<option value="R">Ringo</option>
    255255</select>
    256256>>> print w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
    257 <select multiple="multiple" name="beatles">
     257<select multiple="multiple" name="beatles" id="id_beatles">
    258258<option value="J" selected="selected">John</option>
    259259<option value="P" selected="selected">Paul</option>
    260260<option value="G">George</option>
     
    263263
    264264If the value is None, none of the options are selected:
    265265>>> print w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
    266 <select multiple="multiple" name="beatles">
     266<select multiple="multiple" name="beatles" id="id_beatles">
    267267<option value="J">John</option>
    268268<option value="P">Paul</option>
    269269<option value="G">George</option>
     
    272272
    273273If the value corresponds to a label (but not to an option value), none of the options are selected:
    274274>>> print w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
    275 <select multiple="multiple" name="beatles">
     275<select multiple="multiple" name="beatles" id="id_beatles">
    276276<option value="J">John</option>
    277277<option value="P">Paul</option>
    278278<option value="G">George</option>
     
    281281
    282282If multiple values are given, but some of them are not valid, the valid ones are selected:
    283283>>> print w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
    284 <select multiple="multiple" name="beatles">
     284<select multiple="multiple" name="beatles" id="id_beatles">
    285285<option value="J" selected="selected">John</option>
    286286<option value="P">Paul</option>
    287287<option value="G" selected="selected">George</option>
     
    290290
    291291The value is compared to its str():
    292292>>> print w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')])
    293 <select multiple="multiple" name="nums">
     293<select multiple="multiple" name="nums" id="id_nums">
    294294<option value="1">1</option>
    295295<option value="2" selected="selected">2</option>
    296296<option value="3">3</option>
    297297</select>
    298298>>> print w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)])
    299 <select multiple="multiple" name="nums">
     299<select multiple="multiple" name="nums" id="id_nums">
    300300<option value="1">1</option>
    301301<option value="2" selected="selected">2</option>
    302302<option value="3">3</option>
    303303</select>
    304304>>> print w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)])
    305 <select multiple="multiple" name="nums">
     305<select multiple="multiple" name="nums" id="id_nums">
    306306<option value="1">1</option>
    307307<option value="2" selected="selected">2</option>
    308308<option value="3">3</option>
     
    313313...     for i in range(5):
    314314...         yield (i, i)
    315315>>> print w.render('nums', [2], choices=get_choices())
    316 <select multiple="multiple" name="nums">
     316<select multiple="multiple" name="nums" id="id_nums">
    317317<option value="0">0</option>
    318318<option value="1">1</option>
    319319<option value="2" selected="selected">2</option>
     
    324324You can also pass 'choices' to the constructor:
    325325>>> w = SelectMultiple(choices=[(1, 1), (2, 2), (3, 3)])
    326326>>> print w.render('nums', [2])
    327 <select multiple="multiple" name="nums">
     327<select multiple="multiple" name="nums" id="id_nums">
    328328<option value="1">1</option>
    329329<option value="2" selected="selected">2</option>
    330330<option value="3">3</option>
     
    332332
    333333If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
    334334>>> print w.render('nums', [2], choices=[(4, 4), (5, 5)])
    335 <select multiple="multiple" name="nums">
     335<select multiple="multiple" name="nums" id="id_nums">
    336336<option value="1">1</option>
    337337<option value="2" selected="selected">2</option>
    338338<option value="3">3</option>
     
    771771>>> p = Person()
    772772>>> print p
    773773<table>
    774 <tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
    775 <tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
    776 <tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
     774<tr><td><label for="id_first_name">First name</label>:</td><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
     775<tr><td><label for="id_last_name">Last name</label>:</td><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
     776<tr><td><label for="id_birthday">Birthday</label>:</td><td><input type="text" name="birthday" id="id_birthday" /></td></tr>
    777777</table>
    778778>>> print p.as_table()
    779779<table>
    780 <tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
    781 <tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
    782 <tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
     780<tr><td><label for="id_first_name">First name</label>:</td><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
     781<tr><td><label for="id_last_name">Last name</label>:</td><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
     782<tr><td><label for="id_birthday">Birthday</label>:</td><td><input type="text" name="birthday" id="id_birthday" /></td></tr>
    783783</table>
    784784>>> print p.as_ul()
    785785<ul>
    786 <li>First name: <input type="text" name="first_name" /></li>
    787 <li>Last name: <input type="text" name="last_name" /></li>
    788 <li>Birthday: <input type="text" name="birthday" /></li>
     786<li><label for="id_first_name">First name</label>: <input type="text" name="first_name" id="id_first_name" /></li>
     787<li><label for="id_last_name">Last name</label>: <input type="text" name="last_name" id="id_last_name" /></li>
     788<li><label for="id_birthday">Birthday</label>: <input type="text" name="birthday" id="id_birthday" /></li>
    789789</ul>
    790790>>> print p.as_table_with_errors()
    791791<table>
    792792<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
    793 <tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
     793<tr><td><label for="id_first_name">First name</label>:</td><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
    794794<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
    795 <tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
     795<tr><td><label for="id_last_name">Last name</label>:</td><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
    796796<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
    797 <tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
     797<tr><td><label for="id_birthday">Birthday</label>:</td><td><input type="text" name="birthday" id="id_birthday" /></td></tr>
    798798</table>
    799799>>> print p.as_ul_with_errors()
    800800<ul>
    801 <li><ul><li>This field is required.</li></ul>First name: <input type="text" name="first_name" /></li>
    802 <li><ul><li>This field is required.</li></ul>Last name: <input type="text" name="last_name" /></li>
    803 <li><ul><li>This field is required.</li></ul>Birthday: <input type="text" name="birthday" /></li>
     801<li><ul><li>This field is required.</li></ul><label for="id_first_name">First name</label>: <input type="text" name="first_name" id="id_first_name" /></li>
     802<li><ul><li>This field is required.</li></ul><label for="id_last_name">Last name</label>: <input type="text" name="last_name" id="id_last_name" /></li>
     803<li><ul><li>This field is required.</li></ul><label for="id_birthday">Birthday</label>: <input type="text" name="birthday" id="id_birthday" /></li>
    804804</ul>
    805805
    806806>>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'})
     
    815815>>> p.clean()
    816816{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
    817817>>> print p['first_name']
    818 <input type="text" name="first_name" value="John" />
     818<input type="text" name="first_name" value="John" id="id_first_name" />
    819819>>> print p['last_name']
    820 <input type="text" name="last_name" value="Lennon" />
     820<input type="text" name="last_name" value="Lennon" id="id_last_name" />
    821821>>> print p['birthday']
    822 <input type="text" name="birthday" value="1940-10-9" />
     822<input type="text" name="birthday" value="1940-10-9" id="id_birthday" />
    823823>>> for boundfield in p:
    824824...     print boundfield
    825 <input type="text" name="first_name" value="John" />
    826 <input type="text" name="last_name" value="Lennon" />
    827 <input type="text" name="birthday" value="1940-10-9" />
     825<input type="text" name="first_name" value="John" id="id_first_name" />
     826<input type="text" name="last_name" value="Lennon" id="id_last_name" />
     827<input type="text" name="birthday" value="1940-10-9" id="id_birthday" />
    828828>>> print p
    829829<table>
    830 <tr><td>First name:</td><td><input type="text" name="first_name" value="John" /></td></tr>
    831 <tr><td>Last name:</td><td><input type="text" name="last_name" value="Lennon" /></td></tr>
    832 <tr><td>Birthday:</td><td><input type="text" name="birthday" value="1940-10-9" /></td></tr>
     830<tr><td><label for="id_first_name">First name</label>:</td><td><input type="text" name="first_name" value="John" id="id_first_name" /></td></tr>
     831<tr><td><label for="id_last_name">Last name</label>:</td><td><input type="text" name="last_name" value="Lennon" id="id_last_name" /></td></tr>
     832<tr><td><label for="id_birthday">Birthday</label>:</td><td><input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></td></tr>
    833833</table>
    834834
    835835>>> p = Person({'last_name': u'Lennon'})
     
    856856
    857857>>> p = Person()
    858858>>> print p['first_name']
    859 <input type="text" name="first_name" />
     859<input type="text" name="first_name" id="id_first_name" />
    860860>>> print p['last_name']
    861 <input type="text" name="last_name" />
     861<input type="text" name="last_name" id="id_last_name" />
    862862>>> print p['birthday']
    863 <input type="text" name="birthday" />
     863<input type="text" name="birthday" id="id_birthday" />
    864864
    865865>>> class SignupForm(Form):
    866866...     email = EmailField()
    867867...     get_spam = BooleanField()
    868868>>> f = SignupForm()
    869869>>> print f['email']
    870 <input type="text" name="email" />
     870<input type="text" name="email" id="id_email" />
    871871>>> print f['get_spam']
    872 <input type="checkbox" name="get_spam" />
     872<input type="checkbox" name="get_spam" id="id_get_spam" />
    873873
    874874>>> f = SignupForm({'email': 'test@example.com', 'get_spam': True})
    875875>>> print f['email']
    876 <input type="text" name="email" value="test@example.com" />
     876<input type="text" name="email" value="test@example.com" id="id_email" />
    877877>>> print f['get_spam']
    878 <input checked="checked" type="checkbox" name="get_spam" />
     878<input checked="checked" type="checkbox" name="get_spam" id="id_get_spam" />
    879879
    880880Any Field can have a Widget class passed to its constructor:
    881881>>> class ContactForm(Form):
     
    883883...     message = CharField(widget=Textarea)
    884884>>> f = ContactForm()
    885885>>> print f['subject']
    886 <input type="text" name="subject" />
     886<input type="text" name="subject" id="id_subject" />
    887887>>> print f['message']
    888 <textarea name="message"></textarea>
     888<textarea name="message" id="id_message"></textarea>
    889889
    890890as_textarea() and as_text() are shortcuts for changing the output widget type:
    891891>>> f['subject'].as_textarea()
    892 u'<textarea name="subject"></textarea>'
     892u'<textarea name="subject" id="id_subject"></textarea>'
    893893>>> f['message'].as_text()
    894 u'<input type="text" name="message" />'
     894u'<input type="text" name="message" id="id_message" />'
    895895
    896896The 'widget' parameter to a Field can also be an instance:
    897897>>> class ContactForm(Form):
     
    899899...     message = CharField(widget=Textarea(attrs={'rows': 80, 'cols': 20}))
    900900>>> f = ContactForm()
    901901>>> print f['message']
    902 <textarea rows="80" cols="20" name="message"></textarea>
     902<textarea id="id_message" rows="80" cols="20" name="message"></textarea>
    903903
    904904Instance-level attrs are *not* carried over to as_textarea() and as_text():
    905905>>> f['message'].as_text()
    906 u'<input type="text" name="message" />'
     906u'<input type="text" name="message" id="id_message" />'
    907907>>> f = ContactForm({'subject': 'Hello', 'message': 'I love you.'})
    908908>>> f['subject'].as_textarea()
    909 u'<textarea name="subject">Hello</textarea>'
     909u'<textarea name="subject" id="id_subject">Hello</textarea>'
    910910>>> f['message'].as_text()
    911 u'<input type="text" name="message" value="I love you." />'
     911u'<input type="text" name="message" value="I love you." id="id_message" />'
    912912
    913913For a form with a <select>, use ChoiceField:
    914914>>> class FrameworkForm(Form):
     
    916916...     language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')])
    917917>>> f = FrameworkForm()
    918918>>> print f['language']
    919 <select name="language">
     919<select name="language" id="id_language">
    920920<option value="P">Python</option>
    921921<option value="J">Java</option>
    922922</select>
    923923>>> f = FrameworkForm({'name': 'Django', 'language': 'P'})
    924924>>> print f['language']
    925 <select name="language">
     925<select name="language" id="id_language">
    926926<option value="P" selected="selected">Python</option>
    927927<option value="J">Java</option>
    928928</select>
     
    933933...     composers = MultipleChoiceField()
    934934>>> f = SongForm()
    935935>>> print f['composers']
    936 <select multiple="multiple" name="composers">
     936<select multiple="multiple" name="composers" id="id_composers">
    937937</select>
    938938>>> class SongForm(Form):
    939939...     name = CharField()
    940940...     composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')])
    941941>>> f = SongForm()
    942942>>> print f['composers']
    943 <select multiple="multiple" name="composers">
     943<select multiple="multiple" name="composers" id="id_composers">
    944944<option value="J">John Lennon</option>
    945945<option value="P">Paul McCartney</option>
    946946</select>
    947947>>> f = SongForm({'name': 'Yesterday', 'composers': ['P']})
    948948>>> print f['name']
    949 <input type="text" name="name" value="Yesterday" />
     949<input type="text" name="name" value="Yesterday" id="id_name" />
    950950>>> print f['composers']
    951 <select multiple="multiple" name="composers">
     951<select multiple="multiple" name="composers" id="id_composers">
    952952<option value="J">John Lennon</option>
    953953<option value="P" selected="selected">Paul McCartney</option>
    954954</select>
     
    993993>>> f = UserRegistration()
    994994>>> print f.as_table()
    995995<table>
    996 <tr><td>Username:</td><td><input type="text" name="username" /></td></tr>
    997 <tr><td>Password1:</td><td><input type="password" name="password1" /></td></tr>
    998 <tr><td>Password2:</td><td><input type="password" name="password2" /></td></tr>
     996<tr><td><label for="id_username">Username</label>:</td><td><input type="text" name="username" id="id_username" /></td></tr>
     997<tr><td><label for="id_password1">Password1</label>:</td><td><input type="password" name="password1" id="id_password1" /></td></tr>
     998<tr><td><label for="id_password2">Password2</label>:</td><td><input type="password" name="password2" id="id_password2" /></td></tr>
    999999</table>
    10001000>>> f.errors()
    10011001{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
     
    10041004{'__all__': [u'Please make sure your passwords match.']}
    10051005>>> print f.as_table()
    10061006<table>
    1007 <tr><td>Username:</td><td><input type="text" name="username" value="adrian" /></td></tr>
    1008 <tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></td></tr>
    1009 <tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
     1007<tr><td><label for="id_username">Username</label>:</td><td><input type="text" name="username" value="adrian" id="id_username" /></td></tr>
     1008<tr><td><label for="id_password1">Password1</label>:</td><td><input type="password" name="password1" value="foo" id="id_password1" /></td></tr>
     1009<tr><td><label for="id_password2">Password2</label>:</td><td><input type="password" name="password2" value="bar" id="id_password2" /></td></tr>
    10101010</table>
    10111011>>> print f.as_table_with_errors()
    10121012<table>
    10131013<tr><td colspan="2"><ul><li>Please make sure your passwords match.</li></ul></td></tr>
    1014 <tr><td>Username:</td><td><input type="text" name="username" value="adrian" /></td></tr>
    1015 <tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></td></tr>
    1016 <tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
     1014<tr><td><label for="id_username">Username</label>:</td><td><input type="text" name="username" value="adrian" id="id_username" /></td></tr>
     1015<tr><td><label for="id_password1">Password1</label>:</td><td><input type="password" name="password1" value="foo" id="id_password1" /></td></tr>
     1016<tr><td><label for="id_password2">Password2</label>:</td><td><input type="password" name="password2" value="bar" id="id_password2" /></td></tr>
    10171017</table>
    10181018>>> print f.as_ul_with_errors()
    10191019<ul>
    10201020<li><ul><li>Please make sure your passwords match.</li></ul></li>
    1021 <li>Username: <input type="text" name="username" value="adrian" /></li>
    1022 <li>Password1: <input type="password" name="password1" value="foo" /></li>
    1023 <li>Password2: <input type="password" name="password2" value="bar" /></li>
     1021<li><label for="id_username">Username</label>: <input type="text" name="username" value="adrian" id="id_username" /></li>
     1022<li><label for="id_password1">Password1</label>: <input type="password" name="password1" value="foo" id="id_password1" /></li>
     1023<li><label for="id_password2">Password2</label>: <input type="password" name="password2" value="bar" id="id_password2" /></li>
    10241024</ul>
    10251025>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'})
    10261026>>> f.errors()
Back to Top