Ticket #6630: 00-forms-fieldsets.2.diff

File 00-forms-fieldsets.2.diff, 64.9 KB (added by Petr Marhoun <petr.marhoun@…>, 16 years ago)
  • django/forms/metaclassing.py

    === added file 'django/forms/metaclassing.py'
     
     1"""
     2Functions for form metaclasses. Their name describes their purpose.
     3"""
     4
     5from django.core.exceptions import ImproperlyConfigured
     6from django.utils.datastructures import SortedDict
     7
     8from fields import Field
     9from widgets import media_property
     10
     11def create_meta(cls, attrs):
     12    cls._meta = cls._options(getattr(cls, 'Meta', None))
     13
     14def create_declared_fields(cls, attrs):
     15    fields = []
     16    for name, possible_field in attrs.items():
     17        if isinstance(possible_field, Field):
     18            fields.append((name, possible_field))
     19            delattr(cls, name)
     20    fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
     21    cls.declared_fields = SortedDict(fields)
     22
     23def create_model_fields(cls, attrs):
     24    formfield_callback = attrs.pop('formfield_callback', lambda f: f.formfield())
     25    fields = []
     26    if cls._meta.model:
     27        for dbfield in cls._meta.model._meta.fields + cls._meta.model._meta.many_to_many:
     28            if dbfield.editable:
     29                formfield = formfield_callback(dbfield)
     30                if formfield:
     31                    fields.append((dbfield.name, formfield))
     32    cls.model_fields = SortedDict(fields)
     33
     34def create_base_fields_pool_from_declared_fields(cls, attrs):
     35    fields = []
     36    for base in cls.__mro__[::-1]:
     37        try:
     38            fields += base.declared_fields.items()
     39        except AttributeError:
     40            pass
     41    cls.base_fields_pool = SortedDict(fields)
     42
     43def create_base_fields_pool_from_model_fields_and_declared_fields(cls, attrs):
     44    model_fields, declared_fields = [], []
     45    for base in cls.__mro__[::-1]:
     46        try:
     47            declared_fields += base.declared_fields.items()
     48            if base._meta.model:
     49                model_fields = base.model_fields.items()
     50        except AttributeError:
     51            pass
     52    cls.base_fields_pool = SortedDict(model_fields + declared_fields)
     53
     54def create_base_fields_from_base_fields_pool(cls, attrs):
     55    if (cls._meta.fieldsets is None) + (cls._meta.fields is None) + (cls._meta.exclude is None) < 2:
     56        raise ImproperlyConfigured("%s cannot have more than one option from fieldsets, fields and exclude." % cls.__name__)
     57    if cls._meta.fieldsets:
     58        names = []
     59        for fieldset in cls._meta.fieldsets:
     60            names.extend(fieldset['fields'])
     61    elif cls._meta.fields:
     62        names = cls._meta.fields
     63    elif cls._meta.exclude:
     64        names = [name for name in cls.base_fields_pool if name not in cls._meta.exclude]
     65    else:
     66        names = cls.base_fields_pool.keys()
     67    cls.base_fields = SortedDict([(name, cls.base_fields_pool[name]) for name in names])
     68
     69def create_media(cls, attrs):
     70    if not 'media' in attrs:
     71        cls.media = media_property(cls)
  • django/contrib/auth/forms.py

    === modified file 'django/contrib/auth/forms.py'
     
    1919
    2020    class Meta:
    2121        model = User
    22         fields = ("username",)
     22        fields = ("username", "password1", "password2")
    2323
    2424    def clean_username(self):
    2525        username = self.cleaned_data["username"]
  • django/contrib/auth/tests/views.py

    === modified file 'django/contrib/auth/tests/views.py'
     
    1616        response = self.client.get('/password_reset/')
    1717        self.assertEquals(response.status_code, 200)
    1818        response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'})
    19         self.assertContains(response, "That e-mail address doesn't have an associated user account")
     19        self.assertContains(response, "That e-mail address doesn&#39;t have an associated user account")
    2020        self.assertEquals(len(mail.outbox), 0)
    2121
    2222    def test_email_found(self):
     
    8787        response = self.client.post(path, {'new_password1': 'anewpassword',
    8888                                           'new_password2':' x'})
    8989        self.assertEquals(response.status_code, 200)
    90         self.assert_("The two password fields didn't match" in response.content)
     90        self.assert_("The two password fields didn&#39;t match" in response.content)
    9191
    9292
    9393class ChangePasswordTest(TestCase):
     
    147147            }
    148148        )
    149149        self.assertEquals(response.status_code, 200)
    150         self.assert_("The two password fields didn't match." in response.content)
     150        self.assert_("The two password fields didn&#39;t match." in response.content)
    151151
    152152    def test_password_change_succeeds(self):
    153153        self.login()
  • django/forms/extras/widgets.py

    === modified file 'django/forms/extras/widgets.py'
     
    2424    day_field = '%s_day'
    2525    year_field = '%s_year'
    2626
    27     def __init__(self, attrs=None, years=None):
     27    def __init__(self, attrs=None, years=None, row_attrs=None):
    2828        # years is an optional list/tuple of years to use in the "year" select box.
    29         self.attrs = attrs or {}
     29        super(SelectDateWidget, self).__init__(attrs, row_attrs)
    3030        if years:
    3131            self.years = years
    3232        else:
  • django/forms/forms.py

    === modified file 'django/forms/forms.py'
     
    44
    55from copy import deepcopy
    66
    7 from django.utils.datastructures import SortedDict
    8 from django.utils.html import escape
     7from django.utils.html import conditional_escape
    98from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
    109from django.utils.safestring import mark_safe
    1110
    12 from fields import Field, FileField
    13 from widgets import Media, media_property, TextInput, Textarea
     11from fields import FileField
     12from widgets import Media, TextInput, Textarea
    1413from util import flatatt, ErrorDict, ErrorList, ValidationError
     14import metaclassing
    1515
    1616__all__ = ('BaseForm', 'Form')
    1717
     
    2222    name = name[0].upper() + name[1:]
    2323    return name.replace('_', ' ')
    2424
    25 def get_declared_fields(bases, attrs, with_base_fields=True):
    26     """
    27     Create a list of form field instances from the passed in 'attrs', plus any
    28     similar fields on the base classes (in 'bases'). This is used by both the
    29     Form and ModelForm metclasses.
    30 
    31     If 'with_base_fields' is True, all fields from the bases are used.
    32     Otherwise, only fields in the 'declared_fields' attribute on the bases are
    33     used. The distinction is useful in ModelForm subclassing.
    34     Also integrates any additional media definitions
    35     """
    36     fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
    37     fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
    38 
    39     # If this class is subclassing another Form, add that Form's fields.
    40     # Note that we loop over the bases in *reverse*. This is necessary in
    41     # order to preserve the correct order of fields.
    42     if with_base_fields:
    43         for base in bases[::-1]:
    44             if hasattr(base, 'base_fields'):
    45                 fields = base.base_fields.items() + fields
    46     else:
    47         for base in bases[::-1]:
    48             if hasattr(base, 'declared_fields'):
    49                 fields = base.declared_fields.items() + fields
    50 
    51     return SortedDict(fields)
    52 
    53 class DeclarativeFieldsMetaclass(type):
    54     """
    55     Metaclass that converts Field attributes to a dictionary called
    56     'base_fields', taking into account parent class 'base_fields' as well.
    57     """
     25class FormOptions(object):
     26    def __init__(self, options=None):
     27        self.fieldsets = getattr(options, 'fieldsets', None)
     28        self.fields = getattr(options, 'fields', None)
     29        self.exclude = getattr(options, 'exclude', None)
     30
     31class FormMetaclass(type):
    5832    def __new__(cls, name, bases, attrs):
    59         attrs['base_fields'] = get_declared_fields(bases, attrs)
    60         new_class = super(DeclarativeFieldsMetaclass,
    61                      cls).__new__(cls, name, bases, attrs)
    62         if 'media' not in attrs:
    63             new_class.media = media_property(new_class)
     33        new_class = type.__new__(cls, name, bases, attrs)
     34        metaclassing.create_meta(new_class, attrs)
     35        metaclassing.create_declared_fields(new_class, attrs)
     36        metaclassing.create_base_fields_pool_from_declared_fields(new_class, attrs)
     37        metaclassing.create_base_fields_from_base_fields_pool(new_class, attrs)
     38        metaclassing.create_media(new_class, attrs)
    6439        return new_class
    6540
    6641class BaseForm(StrAndUnicode):
     
    10681        return BoundField(self, field, name)
    10782
    10883    def _get_errors(self):
    109         "Returns an ErrorDict for the data provided for the form"
     84        "Returns an ErrorDict for the data provided for the form."
    11085        if self._errors is None:
    11186            self.full_clean()
    11287        return self._errors
     
    11994        """
    12095        return self.is_bound and not bool(self.errors)
    12196
    122     def add_prefix(self, field_name):
     97    def add_prefix(self, name):
    12398        """
    12499        Returns the field name with a prefix appended, if this Form has a
    125100        prefix set.
    126101
    127102        Subclasses may wish to override.
    128103        """
    129         return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name
    130 
    131     def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):
     104        return self.prefix and ('%s-%s' % (self.prefix, name)) or name
     105
     106    def has_fieldsets(self):
     107        "Returns True if this form has fieldsets."
     108        return bool(self._meta.fieldsets)
     109
     110    def first_fieldset_attrs(self):
     111        "Returns attributes for first fieldset as HTML code."
     112        if self.has_fieldsets() and 'attrs' in self._meta.fieldsets[0]:
     113            return flatatt(self._meta.fieldsets[0]['attrs'])
     114        else:
     115            return u''
     116
     117    def first_fieldset_legend_tag(self):
     118        "Returns legend tag for first fieldset as HTML code."
     119        if self.has_fieldsets() and 'legend' in self._meta.fieldsets[0]:
     120            return mark_safe(u'<legend>%s</legend>' % conditional_escape(force_unicode(self._meta.fieldsets[0]['legend'])))
     121        else:
     122            return u''
     123
     124    def _label_tag_html_output(self, bf, label_tag_html):
     125        "Helper function for outputting HTML from a label. Used by _row_html_output."
     126        label, label_id = bf.label, bf.label_id
     127        if self.label_suffix and label and label[-1] not in ':?.!':
     128            label += self.label_suffix
     129        if label and label_id:
     130            return label_tag_html % {
     131                'label': label,
     132                'id': label_id,
     133            }
     134        else:
     135            return label
     136
     137    def _help_text_html_output(self, bf, help_text_html):
     138        "Helper function for outputting HTML from a help text. Used by _row_html_output."
     139        if bf.help_text:
     140            return help_text_html % {
     141                'help_text': bf.help_text,
     142            }
     143        else:
     144            return u''
     145
     146    def _row_html_output(self, bf, row_html, label_tag_html, help_text_html):
     147        "Helper function for outputting HTML from a widget. Used by _html_output."
     148        return row_html % {
     149            'rendered_widget': unicode(bf),
     150            'rendered_errors': unicode(bf.errors),
     151            'label_tag': self._label_tag_html_output(bf, label_tag_html),
     152            'help_text': self._help_text_html_output(bf, help_text_html),
     153            'attrs': bf.row_attrs,
     154        }
     155
     156    def _top_errors_html_output(self, top_errors, top_errors_html):
     157        "Helper function for outputting HTML from a top errors. Used by _html_output."
     158        return top_errors_html % {
     159            'top_errors': unicode(top_errors),
     160        }
     161
     162    def _fieldset_html_output(self, fields, fieldset, is_first, is_last, fieldset_start_html, fieldset_end_html, legend_tag_html):
     163        "Helper function for outputting HTML from a fieldset. Used by _html_output."
     164        output = []
     165        if not is_first:
     166            legend_tag = attrs = u''
     167            if 'legend' in fieldset:
     168                legend_tag = legend_tag_html % {
     169                    'legend': conditional_escape(force_unicode(fieldset['legend'])),
     170                }
     171            if 'attrs' in fieldset:
     172                attrs = flatatt(fieldset.get('attrs'))
     173            output.append(fieldset_start_html % {
     174                'legend_tag': legend_tag,
     175                'attrs': attrs,
     176            })
     177        for name in fieldset['fields']:
     178            output.append(fields[name])
     179        if not is_last:
     180            output.append(fieldset_end_html)
     181        return u'\n'.join(output)
     182
     183    def _hidden_fields_html_output(self, hidden_fields, hidden_fields_html):
     184        "Helper function for outputting HTML from a hidden fields. Used by _html_output."
     185        return hidden_fields_html % {
     186            'hidden_fields': u''.join(hidden_fields),
     187        }
     188
     189    def _html_output(self, row_html, label_tag_html, help_text_html, top_errors_html,
     190            fieldset_start_html, legend_tag_html, fieldset_end_html, hidden_fields_html):
    132191        "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
    133         top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
    134         output, hidden_fields = [], []
     192        output = []
     193        top_errors, hidden_fields, visible_fields = self.non_field_errors(), [], {}
    135194        for name, field in self.fields.items():
    136195            bf = BoundField(self, field, name)
    137             bf_errors = self.error_class([escape(error) for error in bf.errors]) # Escape and cache in local variable.
    138196            if bf.is_hidden:
    139                 if bf_errors:
    140                     top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors])
     197                if bf.errors:
     198                    top_errors.extend([u'(Hidden field %s) %s' % (name, conditional_escape(force_unicode(e))) for e in bf.errors])
    141199                hidden_fields.append(unicode(bf))
    142200            else:
    143                 if errors_on_separate_row and bf_errors:
    144                     output.append(error_row % force_unicode(bf_errors))
    145                 if bf.label:
    146                     label = escape(force_unicode(bf.label))
    147                     # Only add the suffix if the label does not end in
    148                     # punctuation.
    149                     if self.label_suffix:
    150                         if label[-1] not in ':?.!':
    151                             label += self.label_suffix
    152                     label = bf.label_tag(label) or ''
    153                 else:
    154                     label = ''
    155                 if field.help_text:
    156                     help_text = help_text_html % force_unicode(field.help_text)
    157                 else:
    158                     help_text = u''
    159                 output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text})
     201                visible_fields[name] = self._row_html_output(bf, row_html, label_tag_html, help_text_html)
    160202        if top_errors:
    161             output.insert(0, error_row % force_unicode(top_errors))
    162         if hidden_fields: # Insert any hidden fields in the last row.
    163             str_hidden = u''.join(hidden_fields)
    164             if output:
    165                 last_row = output[-1]
    166                 # Chop off the trailing row_ender (e.g. '</td></tr>') and
    167                 # insert the hidden fields.
    168                 output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender
    169             else:
    170                 # If there aren't any rows in the output, just append the
    171                 # hidden fields.
    172                 output.append(str_hidden)
     203            output.append(self._top_errors_html_output(top_errors, top_errors_html))
     204        if self.has_fieldsets():
     205            for i, fieldset in enumerate(self._meta.fieldsets):
     206                fields = dict((name, visible_fields[name]) for name in fieldset['fields'] if name in visible_fields)
     207                is_first = (i == 0)
     208                is_last = (i + 1 == len(self._meta.fieldsets))
     209                output.append(self._fieldset_html_output(fields, fieldset, is_first, is_last,
     210                    fieldset_start_html, fieldset_end_html, legend_tag_html))
     211        else:
     212            for name in self.fields:
     213                if name in visible_fields:
     214                    output.append(visible_fields[name])
     215        if hidden_fields:
     216            output.append(self._hidden_fields_html_output(hidden_fields, hidden_fields_html))
    173217        return mark_safe(u'\n'.join(output))
    174218
    175219    def as_table(self):
    176220        "Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
    177         return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False)
     221        kwargs = {
     222            'row_html': u'<tr%(attrs)s><th>%(label_tag)s</th><td>%(rendered_errors)s%(rendered_widget)s%(help_text)s</td></tr>',
     223            'label_tag_html': u'<label for="%(id)s">%(label)s</label>',
     224            'help_text_html': u'<br />%(help_text)s',
     225            'top_errors_html': u'<tr><td colspan="2">%(top_errors)s</td></tr>',
     226            'fieldset_start_html': u'<fieldset%(attrs)s>\n%(legend_tag)s<table>',
     227            'fieldset_end_html': u'</table>\n</fieldset>',
     228            'legend_tag_html': u'<legend>%(legend)s</legend>\n',
     229            'hidden_fields_html': u'<tr class="hidden"><td colspan="2">%(hidden_fields)s</td></tr>',
     230        }
     231        return self._html_output(**kwargs)
    178232
    179233    def as_ul(self):
    180234        "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
    181         return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
     235        kwargs = {
     236            'row_html': u'<li%(attrs)s>%(rendered_errors)s%(label_tag)s %(rendered_widget)s%(help_text)s</li>',
     237            'label_tag_html': u'<label for="%(id)s">%(label)s</label>',
     238            'help_text_html': u' %(help_text)s',
     239            'top_errors_html': u'<li>%(top_errors)s</li>',
     240            'fieldset_start_html': u'<fieldset%(attrs)s>\n%(legend_tag)s<ul>',
     241            'fieldset_end_html': u'</ul>\n</fieldset>',
     242            'legend_tag_html': u'<legend>%(legend)s</legend>\n',
     243            'hidden_fields_html': u'<li class="hidden">%(hidden_fields)s</li>',
     244        }
     245        return self._html_output(**kwargs)
    182246
    183247    def as_p(self):
    184248        "Returns this form rendered as HTML <p>s."
    185         return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True)
     249        kwargs = {
     250            'row_html': u'%(rendered_errors)s<p%(attrs)s>%(label_tag)s %(rendered_widget)s%(help_text)s</p>',
     251            'label_tag_html': u'<label for="%(id)s">%(label)s</label>',
     252            'help_text_html': u' %(help_text)s',
     253            'top_errors_html': u'%(top_errors)s',
     254            'fieldset_start_html': u'<fieldset%(attrs)s>\n%(legend_tag)s',
     255            'fieldset_end_html': u'</fieldset>',
     256            'legend_tag_html': u'<legend>%(legend)s</legend>\n',
     257            'hidden_fields_html': u'<p class="hidden">%(hidden_fields)s</p>',
     258        }
     259        return self._html_output(**kwargs)
    186260
    187261    def non_field_errors(self):
    188262        """
     
    221295                    value = getattr(self, 'clean_%s' % name)()
    222296                    self.cleaned_data[name] = value
    223297            except ValidationError, e:
    224                 self._errors[name] = e.messages
     298                self._errors[name] = self.error_class(e.messages)
    225299                if name in self.cleaned_data:
    226300                    del self.cleaned_data[name]
    227301        try:
    228302            self.cleaned_data = self.clean()
    229303        except ValidationError, e:
    230             self._errors[NON_FIELD_ERRORS] = e.messages
     304            self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages)
    231305        if self._errors:
    232306            delattr(self, 'cleaned_data')
    233307
     
    291365    # fancy metaclass stuff purely for the semantic sugar -- it allows one
    292366    # to define a form using declarative syntax.
    293367    # BaseForm itself has no way of designating self.fields.
    294     __metaclass__ = DeclarativeFieldsMetaclass
     368    __metaclass__ = FormMetaclass
     369    _options = FormOptions
    295370
    296371class BoundField(StrAndUnicode):
    297372    "A Field plus data"
    298373    def __init__(self, form, field, name):
    299374        self.form = form
    300375        self.field = field
     376        self.widget = field.widget
     377        self.required = self.field.required
     378        self.is_hidden = self.widget.is_hidden
    301379        self.name = name
    302380        self.html_name = form.add_prefix(name)
    303         if self.field.label is None:
    304             self.label = pretty_name(name)
    305         else:
    306             self.label = self.field.label
    307         self.help_text = field.help_text or ''
    308381
    309382    def __unicode__(self):
    310383        """Renders this field as an HTML widget."""
     
    325398        field's default widget will be used.
    326399        """
    327400        if not widget:
    328             widget = self.field.widget
     401            widget = self.widget
    329402        attrs = attrs or {}
    330403        auto_id = self.auto_id
    331404        if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
     
    358431        """
    359432        Returns the data for this BoundField, or None if it wasn't given.
    360433        """
    361         return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name)
     434        return self.widget.value_from_datadict(self.form.data, self.form.files, self.html_name)
    362435    data = property(_data)
    363436
     437    def _label(self):
     438        "Returns label for this field as safe HTML."
     439        if self.field.label is None:
     440            return pretty_name(self.name)
     441        else:
     442            return conditional_escape(force_unicode(self.field.label))
     443    label = property(_label)
     444
     445    def _label_id(self):
     446        "Returns label id for this field as safe HTML."
     447        id_ = self.widget.attrs.get('id') or self.auto_id
     448        if id_:
     449            return self.widget.id_for_label(id_)
     450    label_id = property(_label_id)
     451
     452    def _help_text(self):
     453        "Returns help text for this field as safe HTML."
     454        if self.field.help_text is None:
     455            return u''
     456        else:
     457            return force_unicode(self.field.help_text)
     458    help_text = property(_help_text)
     459
     460    def _row_attrs(self):
     461        "Returns row attributes for this field as safe HTML."
     462        return flatatt(self.widget.row_attrs)
     463    row_attrs = property(_row_attrs)
     464
    364465    def label_tag(self, contents=None, attrs=None):
    365466        """
    366467        Wraps the given contents in a <label>, if the field has an ID attribute.
     
    369470
    370471        If attrs are given, they're used as HTML attributes on the <label> tag.
    371472        """
    372         contents = contents or escape(self.label)
    373         widget = self.field.widget
    374         id_ = widget.attrs.get('id') or self.auto_id
    375         if id_:
     473        contents = contents or self.label
     474        if self.label_id:
    376475            attrs = attrs and flatatt(attrs) or ''
    377             contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, contents)
     476            contents = '<label for="%s"%s>%s</label>' % (self.label_id, attrs, contents)
    378477        return mark_safe(contents)
    379478
    380     def _is_hidden(self):
    381         "Returns True if this BoundField's widget is hidden."
    382         return self.field.widget.is_hidden
    383     is_hidden = property(_is_hidden)
    384 
    385479    def _auto_id(self):
    386480        """
    387481        Calculates and returns the ID attribute for this BoundField, if the
  • django/forms/models.py

    === modified file 'django/forms/models.py'
     
    88from django.utils.datastructures import SortedDict
    99
    1010from util import ValidationError, ErrorList
    11 from forms import BaseForm, get_declared_fields
     11from forms import FormOptions, FormMetaclass, BaseForm
    1212from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
    1313from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
    14 from widgets import media_property
    1514from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME
     15import metaclassing
    1616
    1717__all__ = (
    1818    'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
     
    147147            field_list.append((f.name, formfield))
    148148    return SortedDict(field_list)
    149149
    150 class ModelFormOptions(object):
     150class ModelFormOptions(FormOptions):
    151151    def __init__(self, options=None):
     152        super(ModelFormOptions, self).__init__(options)
    152153        self.model = getattr(options, 'model', None)
    153         self.fields = getattr(options, 'fields', None)
    154         self.exclude = getattr(options, 'exclude', None)
    155 
    156 
    157 class ModelFormMetaclass(type):
     154
     155class ModelFormMetaclass(FormMetaclass):
    158156    def __new__(cls, name, bases, attrs):
    159         formfield_callback = attrs.pop('formfield_callback',
    160                 lambda f: f.formfield())
    161         try:
    162             parents = [b for b in bases if issubclass(b, ModelForm)]
    163         except NameError:
    164             # We are defining ModelForm itself.
    165             parents = None
    166         declared_fields = get_declared_fields(bases, attrs, False)
    167         new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases,
    168                 attrs)
    169         if not parents:
    170             return new_class
    171 
    172         if 'media' not in attrs:
    173             new_class.media = media_property(new_class)
    174         opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))
    175         if opts.model:
    176             # If a model is defined, extract form fields from it.
    177             fields = fields_for_model(opts.model, opts.fields,
    178                                       opts.exclude, formfield_callback)
    179             # Override default model fields with any custom declared ones
    180             # (plus, include all the other declared fields).
    181             fields.update(declared_fields)
    182         else:
    183             fields = declared_fields
    184         new_class.declared_fields = declared_fields
    185         new_class.base_fields = fields
     157        new_class = type.__new__(cls, name, bases, attrs)
     158        metaclassing.create_meta(new_class, attrs)
     159        metaclassing.create_model_fields(new_class, attrs)
     160        metaclassing.create_declared_fields(new_class, attrs)
     161        metaclassing.create_base_fields_pool_from_model_fields_and_declared_fields(new_class, attrs)
     162        metaclassing.create_base_fields_from_base_fields_pool(new_class, attrs)
     163        metaclassing.create_media(new_class, attrs)
    186164        return new_class
    187165
    188166class BaseModelForm(BaseForm):
     
    196174            object_data = {}
    197175        else:
    198176            self.instance = instance
    199             object_data = model_to_dict(instance, opts.fields, opts.exclude)
     177            object_data = model_to_dict(instance)
    200178        # if initial was provided, it should override the values from instance
    201179        if initial is not None:
    202180            object_data.update(initial)
     
    215193            fail_message = 'created'
    216194        else:
    217195            fail_message = 'changed'
    218         return save_instance(self, self.instance, self._meta.fields, fail_message, commit)
     196        return save_instance(self, self.instance, self.fields.keys(), fail_message, commit)
    219197
    220198class ModelForm(BaseModelForm):
    221199    __metaclass__ = ModelFormMetaclass
     200    _options = ModelFormOptions
    222201
    223202def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
    224203                       formfield_callback=lambda f: f.formfield()):
  • django/forms/util.py

    === modified file 'django/forms/util.py'
     
    3939    def as_ul(self):
    4040        if not self: return u''
    4141        return mark_safe(u'<ul class="errorlist">%s</ul>'
    42                 % ''.join([u'<li>%s</li>' % force_unicode(e) for e in self]))
     42                % ''.join([u'<li>%s</li>' % conditional_escape(force_unicode(e)) for e in self]))
    4343
    4444    def as_text(self):
    4545        if not self: return u''
  • django/forms/widgets.py

    === modified file 'django/forms/widgets.py'
     
    133133    is_hidden = False          # Determines whether this corresponds to an <input type="hidden">.
    134134    needs_multipart_form = False # Determines does this widget need multipart-encrypted form
    135135
    136     def __init__(self, attrs=None):
     136    def __init__(self, attrs=None, row_attrs=None):
    137137        if attrs is not None:
    138138            self.attrs = attrs.copy()
    139139        else:
    140140            self.attrs = {}
     141        if row_attrs is not None:
     142            self.row_attrs = row_attrs.copy()
     143        else:
     144            self.row_attrs = {}
    141145
    142146    def __deepcopy__(self, memo):
    143147        obj = copy.copy(self)
    144148        obj.attrs = self.attrs.copy()
     149        obj.row_attrs = self.row_attrs.copy()
    145150        memo[id(self)] = obj
    146151        return obj
    147152
     
    221226class PasswordInput(Input):
    222227    input_type = 'password'
    223228
    224     def __init__(self, attrs=None, render_value=True):
    225         super(PasswordInput, self).__init__(attrs)
     229    def __init__(self, attrs=None, render_value=True, row_attrs=None):
     230        super(PasswordInput, self).__init__(attrs, row_attrs)
    226231        self.render_value = render_value
    227232
    228233    def render(self, name, value, attrs=None):
     
    238243    A widget that handles <input type="hidden"> for fields that have a list
    239244    of values.
    240245    """
    241     def __init__(self, attrs=None, choices=()):
    242         super(MultipleHiddenInput, self).__init__(attrs)
     246    def __init__(self, attrs=None, choices=(), row_attrs=None):
     247        super(MultipleHiddenInput, self).__init__(attrs, row_attrs)
    243248        # choices can be any iterable
    244249        self.choices = choices
    245250
     
    272277        return True
    273278
    274279class Textarea(Widget):
    275     def __init__(self, attrs=None):
     280    def __init__(self, attrs=None, row_attrs=None):
    276281        # The 'rows' and 'cols' attributes are required for HTML correctness.
    277         self.attrs = {'cols': '40', 'rows': '10'}
     282        default_attrs = {'cols': '40', 'rows': '10'}
    278283        if attrs:
    279             self.attrs.update(attrs)
     284            default_attrs.update(attrs)
     285        super(Textarea, self).__init__(default_attrs, row_attrs)
    280286
    281287    def render(self, name, value, attrs=None):
    282288        if value is None: value = ''
     
    289295    input_type = 'text'
    290296    format = '%Y-%m-%d %H:%M:%S'     # '2006-10-25 14:30:59'
    291297
    292     def __init__(self, attrs=None, format=None):
    293         super(DateTimeInput, self).__init__(attrs)
     298    def __init__(self, attrs=None, format=None, row_attrs=None):
     299        super(DateTimeInput, self).__init__(attrs, row_attrs)
    294300        if format:
    295301            self.format = format
    296302
     
    313319        return super(TimeInput, self).render(name, value, attrs)
    314320
    315321class CheckboxInput(Widget):
    316     def __init__(self, attrs=None, check_test=bool):
    317         super(CheckboxInput, self).__init__(attrs)
     322    def __init__(self, attrs=None, check_test=bool, row_attrs=None):
     323        super(CheckboxInput, self).__init__(attrs, row_attrs)
    318324        # check_test is a callable that takes a value and returns True
    319325        # if the checkbox should be checked for that value.
    320326        self.check_test = check_test
     
    345351        return bool(initial) != bool(data)
    346352
    347353class Select(Widget):
    348     def __init__(self, attrs=None, choices=()):
    349         super(Select, self).__init__(attrs)
     354    def __init__(self, attrs=None, choices=(), row_attrs=None):
     355        super(Select, self).__init__(attrs, row_attrs)
    350356        # choices can be any iterable, but we may need to render this widget
    351357        # multiple times. Thus, collapse it into a list so it can be consumed
    352358        # more than once.
     
    386392    """
    387393    A Select Widget intended to be used with NullBooleanField.
    388394    """
    389     def __init__(self, attrs=None):
     395    def __init__(self, attrs=None, row_attrs=None):
    390396        choices = ((u'1', ugettext('Unknown')), (u'2', ugettext('Yes')), (u'3', ugettext('No')))
    391         super(NullBooleanSelect, self).__init__(attrs, choices)
     397        super(NullBooleanSelect, self).__init__(attrs, choices, row_attrs)
    392398
    393399    def render(self, name, value, attrs=None, choices=()):
    394400        try:
     
    581587
    582588    You'll probably want to use this class with MultiValueField.
    583589    """
    584     def __init__(self, widgets, attrs=None):
     590    def __init__(self, widgets, attrs=None, row_attrs=None):
    585591        self.widgets = [isinstance(w, type) and w() or w for w in widgets]
    586         super(MultiWidget, self).__init__(attrs)
     592        super(MultiWidget, self).__init__(attrs, row_attrs)
    587593
    588594    def render(self, name, value, attrs=None):
    589595        # value is a list of values, each corresponding to a widget
     
    653659    """
    654660    A Widget that splits datetime input into two <input type="text"> boxes.
    655661    """
    656     def __init__(self, attrs=None):
     662    def __init__(self, attrs=None, row_attrs=None):
    657663        widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs))
    658         super(SplitDateTimeWidget, self).__init__(widgets, attrs)
     664        super(SplitDateTimeWidget, self).__init__(widgets, attrs, row_attrs)
    659665
    660666    def decompress(self, value):
    661667        if value:
  • tests/modeltests/generic_relations/models.py

    === modified file 'tests/modeltests/generic_relations/models.py'
     
    214214>>> for form in formset.forms:
    215215...     print form.as_p()
    216216<p><label for="id_generic_relations-taggeditem-content_type-object_id-0-tag">Tag:</label> <input id="id_generic_relations-taggeditem-content_type-object_id-0-tag" type="text" name="generic_relations-taggeditem-content_type-object_id-0-tag" maxlength="50" /></p>
    217 <p><label for="id_generic_relations-taggeditem-content_type-object_id-0-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-0-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-0-DELETE" /><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-0-id" id="id_generic_relations-taggeditem-content_type-object_id-0-id" /></p>
     217<p><label for="id_generic_relations-taggeditem-content_type-object_id-0-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-0-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-0-DELETE" /></p>
     218<p class="hidden"><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-0-id" id="id_generic_relations-taggeditem-content_type-object_id-0-id" /></p>
    218219
    219220>>> formset = GenericFormSet(instance=platypus)
    220221>>> for form in formset.forms:
    221222...     print form.as_p()
    222223<p><label for="id_generic_relations-taggeditem-content_type-object_id-0-tag">Tag:</label> <input id="id_generic_relations-taggeditem-content_type-object_id-0-tag" type="text" name="generic_relations-taggeditem-content_type-object_id-0-tag" value="shiny" maxlength="50" /></p>
    223 <p><label for="id_generic_relations-taggeditem-content_type-object_id-0-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-0-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-0-DELETE" /><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-0-id" value="..." id="id_generic_relations-taggeditem-content_type-object_id-0-id" /></p>
     224<p><label for="id_generic_relations-taggeditem-content_type-object_id-0-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-0-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-0-DELETE" /></p>
     225<p class="hidden"><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-0-id" value="..." id="id_generic_relations-taggeditem-content_type-object_id-0-id" /></p>
    224226<p><label for="id_generic_relations-taggeditem-content_type-object_id-1-tag">Tag:</label> <input id="id_generic_relations-taggeditem-content_type-object_id-1-tag" type="text" name="generic_relations-taggeditem-content_type-object_id-1-tag" maxlength="50" /></p>
    225 <p><label for="id_generic_relations-taggeditem-content_type-object_id-1-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-1-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-1-DELETE" /><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-1-id" id="id_generic_relations-taggeditem-content_type-object_id-1-id" /></p>
     227<p><label for="id_generic_relations-taggeditem-content_type-object_id-1-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-1-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-1-DELETE" /></p>
     228<p class="hidden"><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-1-id" id="id_generic_relations-taggeditem-content_type-object_id-1-id" /></p>
    226229
    227230"""}
  • tests/modeltests/model_forms/models.py

    === modified file 'tests/modeltests/model_forms/models.py'
     
    175175...         model = Category
    176176...         fields = ['name', 'url']
    177177...         exclude = ['url']
    178 
    179 >>> CategoryForm.base_fields.keys()
    180 ['name']
     178Traceback (most recent call last):
     179  File "/home/petr/django/local2/00-forms-fieldsets/django/test/_doctest.py", line 1267, in __run
     180    compileflags, 1) in test.globs
     181  File "<doctest modeltests.model_forms.models.__test__.API_TESTS[12]>", line 1, in ?
     182    class CategoryForm(ModelForm):
     183  File "/home/petr/django/local2/00-forms-fieldsets/django/forms/models.py", line 220, in __new__
     184    metaclassing.create_base_fields_from_base_fields_pool(new_class)
     185  File "/home/petr/django/local2/00-forms-fieldsets/django/forms/metaclassing.py", line 50, in create_base_fields_from_base_fields_pool
     186    raise ImproperlyConfigured("%s cannot have more than one option from fieldsets, fields and exclude." % cls.__name__)
     187ImproperlyConfigured: CategoryForm cannot have more than one option from fieldsets, fields and exclude.
    181188
    182189Don't allow more than one 'model' definition in the inheritance hierarchy.
    183190Technically, it would generate a valid form, but the fact that the resulting
  • tests/modeltests/model_formsets/models.py

    === modified file 'tests/modeltests/model_formsets/models.py'
     
    7272>>> formset = AuthorFormSet(queryset=qs)
    7373>>> for form in formset.forms:
    7474...     print form.as_p()
    75 <p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /><input type="hidden" name="form-0-id" id="id_form-0-id" /></p>
    76 <p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" maxlength="100" /><input type="hidden" name="form-1-id" id="id_form-1-id" /></p>
    77 <p><label for="id_form-2-name">Name:</label> <input id="id_form-2-name" type="text" name="form-2-name" maxlength="100" /><input type="hidden" name="form-2-id" id="id_form-2-id" /></p>
     75<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></p>
     76<p class ="hidden"><input type="hidden" name="form-0-id" id="id_form-0-id" /></p>
     77<p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" maxlength="100" /></p>
     78<p class ="hidden"><input type="hidden" name="form-1-id" id="id_form-1-id" /></p>
     79<p><label for="id_form-2-name">Name:</label> <input id="id_form-2-name" type="text" name="form-2-name" maxlength="100" /></p>
     80<p class ="hidden"><input type="hidden" name="form-2-id" id="id_form-2-id" /></p>
    7881
    7982>>> data = {
    8083...     'form-TOTAL_FORMS': '3', # the number of forms rendered
     
    108111>>> formset = AuthorFormSet(queryset=qs)
    109112>>> for form in formset.forms:
    110113...     print form.as_p()
    111 <p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Arthur Rimbaud" maxlength="100" /><input type="hidden" name="form-0-id" value="2" id="id_form-0-id" /></p>
    112 <p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" value="Charles Baudelaire" maxlength="100" /><input type="hidden" name="form-1-id" value="1" id="id_form-1-id" /></p>
    113 <p><label for="id_form-2-name">Name:</label> <input id="id_form-2-name" type="text" name="form-2-name" maxlength="100" /><input type="hidden" name="form-2-id" id="id_form-2-id" /></p>
     114<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Arthur Rimbaud" maxlength="100" /></p>
     115<p class ="hidden"><input type="hidden" name="form-0-id" value="2" id="id_form-0-id" /></p>
     116<p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" value="Charles Baudelaire" maxlength="100" /></p>
     117<p class ="hidden"><input type="hidden" name="form-1-id" value="1" id="id_form-1-id" /></p>
     118<p><label for="id_form-2-name">Name:</label> <input id="id_form-2-name" type="text" name="form-2-name" maxlength="100" /></p>
     119<p class ="hidden"><input type="hidden" name="form-2-id" id="id_form-2-id" /></p>
    114120
    115121
    116122>>> data = {
     
    148154>>> for form in formset.forms:
    149155...     print form.as_p()
    150156<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Arthur Rimbaud" maxlength="100" /></p>
    151 <p><label for="id_form-0-DELETE">Delete:</label> <input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /><input type="hidden" name="form-0-id" value="2" id="id_form-0-id" /></p>
     157<p><label for="id_form-0-DELETE">Delete:</label> <input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /></p>
     158<p class ="hidden"><input type="hidden" name="form-0-id" value="2" id="id_form-0-id" /></p>
    152159<p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" value="Charles Baudelaire" maxlength="100" /></p>
    153 <p><label for="id_form-1-DELETE">Delete:</label> <input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE" /><input type="hidden" name="form-1-id" value="1" id="id_form-1-id" /></p>
     160<p><label for="id_form-1-DELETE">Delete:</label> <input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE" /></p>
     161<p class ="hidden"><input type="hidden" name="form-1-id" value="1" id="id_form-1-id" /></p>
    154162<p><label for="id_form-2-name">Name:</label> <input id="id_form-2-name" type="text" name="form-2-name" value="Paul Verlaine" maxlength="100" /></p>
    155 <p><label for="id_form-2-DELETE">Delete:</label> <input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE" /><input type="hidden" name="form-2-id" value="3" id="id_form-2-id" /></p>
     163<p><label for="id_form-2-DELETE">Delete:</label> <input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE" /></p>
     164<p class ="hidden"><input type="hidden" name="form-2-id" value="3" id="id_form-2-id" /></p>
    156165<p><label for="id_form-3-name">Name:</label> <input id="id_form-3-name" type="text" name="form-3-name" maxlength="100" /></p>
    157 <p><label for="id_form-3-DELETE">Delete:</label> <input type="checkbox" name="form-3-DELETE" id="id_form-3-DELETE" /><input type="hidden" name="form-3-id" id="id_form-3-id" /></p>
     166<p><label for="id_form-3-DELETE">Delete:</label> <input type="checkbox" name="form-3-DELETE" id="id_form-3-DELETE" /></p>
     167<p class ="hidden"><input type="hidden" name="form-3-id" id="id_form-3-id" /></p>
    158168
    159169>>> data = {
    160170...     'form-TOTAL_FORMS': '4', # the number of forms rendered
     
    262272>>> for form in formset.forms:
    263273...     print form.as_p()
    264274<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></p>
    265 <p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr_id" id="id_form-0-author_ptr_id" /></p>
     275<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" id="id_form-0-write_speed" /></p>
     276<p class="hidden"><input type="hidden" name="form-0-author_ptr_id" id="id_form-0-author_ptr_id" /></p>
    266277
    267278>>> data = {
    268279...     'form-TOTAL_FORMS': '1', # the number of forms rendered
     
    283294>>> for form in formset.forms:
    284295...     print form.as_p()
    285296<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Ernest Hemingway" maxlength="100" /></p>
    286 <p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" value="10" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr_id" value="..." id="id_form-0-author_ptr_id" /></p>
     297<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" value="10" id="id_form-0-write_speed" /></p>
     298<p class="hidden"><input type="hidden" name="form-0-author_ptr_id" value="..." id="id_form-0-author_ptr_id" /></p>
    287299<p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" maxlength="100" /></p>
    288 <p><label for="id_form-1-write_speed">Write speed:</label> <input type="text" name="form-1-write_speed" id="id_form-1-write_speed" /><input type="hidden" name="form-1-author_ptr_id" id="id_form-1-author_ptr_id" /></p>
     300<p><label for="id_form-1-write_speed">Write speed:</label> <input type="text" name="form-1-write_speed" id="id_form-1-write_speed" /></p>
     301<p class="hidden"><input type="hidden" name="form-1-author_ptr_id" id="id_form-1-author_ptr_id" /></p>
    289302
    290303>>> data = {
    291304...     'form-TOTAL_FORMS': '2', # the number of forms rendered
     
    317330>>> formset = AuthorBooksFormSet(instance=author)
    318331>>> for form in formset.forms:
    319332...     print form.as_p()
    320 <p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" maxlength="100" /><input type="hidden" name="book_set-0-id" id="id_book_set-0-id" /></p>
    321 <p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p>
    322 <p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p>
     333<p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" maxlength="100" /></p>
     334<p class ="hidden"><input type="hidden" name="book_set-0-id" id="id_book_set-0-id" /></p>
     335<p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /></p>
     336<p class ="hidden"><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p>
     337<p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /></p>
     338<p class ="hidden"><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p>
    323339
    324340>>> data = {
    325341...     'book_set-TOTAL_FORMS': '3', # the number of forms rendered
     
    351367>>> formset = AuthorBooksFormSet(instance=author)
    352368>>> for form in formset.forms:
    353369...     print form.as_p()
    354 <p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Les Fleurs du Mal" maxlength="100" /><input type="hidden" name="book_set-0-id" value="1" id="id_book_set-0-id" /></p>
    355 <p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p>
    356 <p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p>
     370<p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Les Fleurs du Mal" maxlength="100" /></p>
     371<p class ="hidden"><input type="hidden" name="book_set-0-id" value="1" id="id_book_set-0-id" /></p>
     372<p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /></p>
     373<p class ="hidden"><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p>
     374<p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /></p>
     375<p class ="hidden"><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p>
    357376
    358377>>> data = {
    359378...     'book_set-TOTAL_FORMS': '3', # the number of forms rendered
     
    405424>>> formset = AuthorBooksFormSet(prefix="test")
    406425>>> for form in formset.forms:
    407426...     print form.as_p()
    408 <p><label for="id_test-0-title">Title:</label> <input id="id_test-0-title" type="text" name="test-0-title" maxlength="100" /><input type="hidden" name="test-0-id" id="id_test-0-id" /></p>
    409 <p><label for="id_test-1-title">Title:</label> <input id="id_test-1-title" type="text" name="test-1-title" maxlength="100" /><input type="hidden" name="test-1-id" id="id_test-1-id" /></p>
     427<p><label for="id_test-0-title">Title:</label> <input id="id_test-0-title" type="text" name="test-0-title" maxlength="100" /></p>
     428<p class ="hidden"><input type="hidden" name="test-0-id" id="id_test-0-id" /></p>
     429<p><label for="id_test-1-title">Title:</label> <input id="id_test-1-title" type="text" name="test-1-title" maxlength="100" /></p>
     430<p class ="hidden"><input type="hidden" name="test-1-id" id="id_test-1-id" /></p>
    410431
    411432# Test a custom primary key ###################################################
    412433
  • tests/regressiontests/forms/extra.py

    === modified file 'tests/regressiontests/forms/extra.py'
     
    439439>>> f = CommentForm(data, auto_id=False, error_class=DivErrorList)
    440440>>> print f.as_p()
    441441<p>Name: <input type="text" name="name" maxlength="50" /></p>
    442 <div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div>
    443 <p>Email: <input type="text" name="email" value="invalid" /></p>
    444 <div class="errorlist"><div class="error">This field is required.</div></div>
    445 <p>Comment: <input type="text" name="comment" /></p>
     442<div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div><p>Email: <input type="text" name="email" value="invalid" /></p>
     443<div class="errorlist"><div class="error">This field is required.</div></div><p>Comment: <input type="text" name="comment" /></p>
    446444
    447445#################################
    448446# Test multipart-encoded form #
  • tests/regressiontests/forms/fields.py

    === modified file 'tests/regressiontests/forms/fields.py'
     
    10971097...     hidden_nullbool2 = NullBooleanField(widget=HiddenInput, initial=False)
    10981098>>> f = HiddenNullBooleanForm()
    10991099>>> print f
    1100 <input type="hidden" name="hidden_nullbool1" value="True" id="id_hidden_nullbool1" /><input type="hidden" name="hidden_nullbool2" value="False" id="id_hidden_nullbool2" />
     1100<tr class="hidden"><td colspan="2"><input type="hidden" name="hidden_nullbool1" value="True" id="id_hidden_nullbool1" /><input type="hidden" name="hidden_nullbool2" value="False" id="id_hidden_nullbool2" /></td></tr>
    11011101>>> f = HiddenNullBooleanForm({ 'hidden_nullbool1': 'True', 'hidden_nullbool2': 'False' })
    11021102>>> f.full_clean()
    11031103>>> f.cleaned_data['hidden_nullbool1']
  • tests/regressiontests/forms/forms.py

    === modified file 'tests/regressiontests/forms/forms.py'
     
    9494<li><ul class="errorlist"><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>
    9595<li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li>
    9696>>> print p.as_p()
    97 <ul class="errorlist"><li>This field is required.</li></ul>
    98 <p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p>
    99 <ul class="errorlist"><li>This field is required.</li></ul>
    100 <p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p>
    101 <ul class="errorlist"><li>This field is required.</li></ul>
    102 <p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p>
     97<ul class="errorlist"><li>This field is required.</li></ul><p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p>
     98<ul class="errorlist"><li>This field is required.</li></ul><p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p>
     99<ul class="errorlist"><li>This field is required.</li></ul><p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p>
    103100
    104101If you don't pass any values to the Form's __init__(), or if you pass None,
    105102the Form will be considered unbound and won't do any validation. Form.errors
     
    569566...     composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=MultipleHiddenInput)
    570567>>> f = SongFormHidden(MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])), auto_id=False)
    571568>>> print f.as_ul()
    572 <li>Name: <input type="text" name="name" value="Yesterday" /><input type="hidden" name="composers" value="J" />
     569<li>Name: <input type="text" name="name" value="Yesterday" /></li>
     570<li class ="hidden"><input type="hidden" name="composers" value="J" />
    573571<input type="hidden" name="composers" value="P" /></li>
    574572
    575573When using CheckboxSelectMultiple, the framework expects a list of input and
     
    814812>>> print p
    815813<tr><th>First name:</th><td><input type="text" name="first_name" /></td></tr>
    816814<tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr>
    817 <tr><th>Birthday:</th><td><input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></td></tr>
     815<tr><th>Birthday:</th><td><input type="text" name="birthday" /></td></tr>
     816<tr class="hidden"><td colspan="2"><input type="hidden" name="hidden_text" /></td></tr>
    818817>>> print p.as_ul()
    819818<li>First name: <input type="text" name="first_name" /></li>
    820819<li>Last name: <input type="text" name="last_name" /></li>
    821 <li>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></li>
     820<li>Birthday: <input type="text" name="birthday" /></li>
     821<li class ="hidden"><input type="hidden" name="hidden_text" /></li>
    822822>>> print p.as_p()
    823823<p>First name: <input type="text" name="first_name" /></p>
    824824<p>Last name: <input type="text" name="last_name" /></p>
    825 <p>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></p>
     825<p>Birthday: <input type="text" name="birthday" /></p>
     826<p class ="hidden"><input type="hidden" name="hidden_text" /></p>
    826827
    827828With auto_id set, a HiddenInput still gets an ID, but it doesn't get a label.
    828829>>> p = Person(auto_id='id_%s')
    829830>>> print p
    830831<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
    831832<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
    832 <tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></td></tr>
     833<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" id="id_birthday" /></td></tr>
     834<tr class="hidden"><td colspan="2"><input type="hidden" name="hidden_text" id="id_hidden_text" /></td></tr>
    833835>>> print p.as_ul()
    834836<li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li>
    835837<li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li>
    836 <li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></li>
     838<li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li>
     839<li class ="hidden"><input type="hidden" name="hidden_text" id="id_hidden_text" /></li>
    837840>>> print p.as_p()
    838841<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p>
    839842<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p>
    840 <p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></p>
     843<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p>
     844<p class ="hidden"><input type="hidden" name="hidden_text" id="id_hidden_text" /></p>
    841845
    842846If a field with a HiddenInput has errors, the as_table() and as_ul() output
    843847will include the error message(s) with the text "(Hidden field [fieldname]) "
     
    848852<tr><td colspan="2"><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></td></tr>
    849853<tr><th>First name:</th><td><input type="text" name="first_name" value="John" /></td></tr>
    850854<tr><th>Last name:</th><td><input type="text" name="last_name" value="Lennon" /></td></tr>
    851 <tr><th>Birthday:</th><td><input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></td></tr>
     855<tr><th>Birthday:</th><td><input type="text" name="birthday" value="1940-10-9" /></td></tr>
     856<tr class="hidden"><td colspan="2"><input type="hidden" name="hidden_text" /></td></tr>
    852857>>> print p.as_ul()
    853858<li><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></li>
    854859<li>First name: <input type="text" name="first_name" value="John" /></li>
    855860<li>Last name: <input type="text" name="last_name" value="Lennon" /></li>
    856 <li>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></li>
     861<li>Birthday: <input type="text" name="birthday" value="1940-10-9" /></li>
     862<li class ="hidden"><input type="hidden" name="hidden_text" /></li>
    857863>>> print p.as_p()
    858864<ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul>
    859865<p>First name: <input type="text" name="first_name" value="John" /></p>
    860866<p>Last name: <input type="text" name="last_name" value="Lennon" /></p>
    861 <p>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></p>
     867<p>Birthday: <input type="text" name="birthday" value="1940-10-9" /></p>
     868<p class ="hidden"><input type="hidden" name="hidden_text" /></p>
    862869
    863870A corner case: It's possible for a form to have only HiddenInputs.
    864871>>> class TestForm(Form):
     
    866873...     bar = CharField(widget=HiddenInput)
    867874>>> p = TestForm(auto_id=False)
    868875>>> print p.as_table()
    869 <input type="hidden" name="foo" /><input type="hidden" name="bar" />
     876<tr class="hidden"><td colspan="2"><input type="hidden" name="foo" /><input type="hidden" name="bar" /></td></tr>
    870877>>> print p.as_ul()
    871 <input type="hidden" name="foo" /><input type="hidden" name="bar" />
     878<li class ="hidden"><input type="hidden" name="foo" /><input type="hidden" name="bar" /></li>
    872879>>> print p.as_p()
    873 <input type="hidden" name="foo" /><input type="hidden" name="bar" />
     880<p class ="hidden"><input type="hidden" name="foo" /><input type="hidden" name="bar" /></p>
    874881
    875882A Form's fields are displayed in the same order in which they were defined.
    876883>>> class TestForm(Form):
     
    12211228>>> p = UserRegistration(auto_id=False)
    12221229>>> print p.as_ul()
    12231230<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li>
    1224 <li>Password: <input type="password" name="password" /><input type="hidden" name="next" value="/" /></li>
     1231<li>Password: <input type="password" name="password" /></li>
     1232<li class ="hidden"><input type="hidden" name="next" value="/" /></li>
    12251233
    12261234Help text can include arbitrary Unicode characters.
    12271235>>> class UserRegistration(Form):
     
    12651273...     haircut_type = CharField()
    12661274>>> b = Beatle(auto_id=False)
    12671275>>> print b.as_ul()
     1276<li>Instrument: <input type="text" name="instrument" /></li>
    12681277<li>First name: <input type="text" name="first_name" /></li>
    12691278<li>Last name: <input type="text" name="last_name" /></li>
    12701279<li>Birthday: <input type="text" name="birthday" /></li>
    1271 <li>Instrument: <input type="text" name="instrument" /></li>
    12721280<li>Haircut type: <input type="text" name="haircut_type" /></li>
    12731281
    12741282# Forms with prefixes #########################################################
  • tests/regressiontests/forms/formsets.py

    === modified file 'tests/regressiontests/forms/formsets.py'
     
    2020
    2121>>> formset = ChoiceFormSet(auto_id=False, prefix='choices')
    2222>>> print formset
    23 <input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" />
     23<tr class="hidden"><td colspan="2"><input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /></td></tr>
    2424<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr>
    2525<tr><th>Votes:</th><td><input type="text" name="choices-0-votes" /></td></tr>
    2626
  • tests/regressiontests/forms/regressions.py

    === modified file 'tests/regressiontests/forms/regressions.py'
     
    5656>>> activate('ru')
    5757>>> f = SomeForm({})
    5858>>> f.as_p()
    59 u'<ul class="errorlist"><li>\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label> <ul>\n<li><label for="id_somechoice_0"><input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> En tied\xe4</label></li>\n<li><label for="id_somechoice_1"><input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> Mies</label></li>\n<li><label for="id_somechoice_2"><input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> Nainen</label></li>\n</ul></p>'
     59u'<ul class="errorlist"><li>\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul><p><label for="id_somechoice_0">\xc5\xf8\xdf:</label> <ul>\n<li><label for="id_somechoice_0"><input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> En tied\xe4</label></li>\n<li><label for="id_somechoice_1"><input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> Mies</label></li>\n<li><label for="id_somechoice_2"><input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> Nainen</label></li>\n</ul></p>'
    6060>>> deactivate()
    6161
    6262Deep copying translated text shouldn't raise an error
  • tests/regressiontests/forms/util.py

    === modified file 'tests/regressiontests/forms/util.py'
     
    4949# Can take a non-string.
    5050>>> print ValidationError(VeryBadError()).messages
    5151<ul class="errorlist"><li>A very bad error.</li></ul>
     52
     53# Can escape and conditional escape.
     54
     55>>> from django.utils.html import escape, conditional_escape
     56>>> example = 'Example of link: <a href="http://www.example.com/">example</a>'
     57
     58>>> print ValidationError(example).messages
     59<ul class="errorlist"><li>Example of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>
     60>>> print ValidationError(escape(example)).messages
     61<ul class="errorlist"><li>Example of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>
     62>>> print ValidationError(conditional_escape(example)).messages
     63<ul class="errorlist"><li>Example of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>
     64>>> print ValidationError(mark_safe(example)).messages
     65<ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></li></ul>
     66
     67>>> print conditional_escape(unicode(ValidationError(example).messages))
     68<ul class="errorlist"><li>Example of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>
     69>>> print conditional_escape(unicode(ValidationError(escape(example)).messages))
     70<ul class="errorlist"><li>Example of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>
     71>>> print conditional_escape(unicode(ValidationError(conditional_escape(example)).messages))
     72<ul class="errorlist"><li>Example of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>
     73>>> print conditional_escape(unicode(ValidationError(mark_safe(example)).messages))
     74<ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></li></ul>
    5275"""
Back to Top