Ticket #6630: 00-forms-fieldsets.2.diff
File 00-forms-fieldsets.2.diff, 64.9 KB (added by , 16 years ago) |
---|
-
django/forms/metaclassing.py
=== added file 'django/forms/metaclassing.py'
1 """ 2 Functions for form metaclasses. Their name describes their purpose. 3 """ 4 5 from django.core.exceptions import ImproperlyConfigured 6 from django.utils.datastructures import SortedDict 7 8 from fields import Field 9 from widgets import media_property 10 11 def create_meta(cls, attrs): 12 cls._meta = cls._options(getattr(cls, 'Meta', None)) 13 14 def 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 23 def 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 34 def 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 43 def 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 54 def 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 69 def 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'
19 19 20 20 class Meta: 21 21 model = User 22 fields = ("username", )22 fields = ("username", "password1", "password2") 23 23 24 24 def clean_username(self): 25 25 username = self.cleaned_data["username"] -
django/contrib/auth/tests/views.py
=== modified file 'django/contrib/auth/tests/views.py'
16 16 response = self.client.get('/password_reset/') 17 17 self.assertEquals(response.status_code, 200) 18 18 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't have an associated user account") 20 20 self.assertEquals(len(mail.outbox), 0) 21 21 22 22 def test_email_found(self): … … 87 87 response = self.client.post(path, {'new_password1': 'anewpassword', 88 88 'new_password2':' x'}) 89 89 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't match" in response.content) 91 91 92 92 93 93 class ChangePasswordTest(TestCase): … … 147 147 } 148 148 ) 149 149 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't match." in response.content) 151 151 152 152 def test_password_change_succeeds(self): 153 153 self.login() -
django/forms/extras/widgets.py
=== modified file 'django/forms/extras/widgets.py'
24 24 day_field = '%s_day' 25 25 year_field = '%s_year' 26 26 27 def __init__(self, attrs=None, years=None ):27 def __init__(self, attrs=None, years=None, row_attrs=None): 28 28 # years is an optional list/tuple of years to use in the "year" select box. 29 s elf.attrs = attrs or {}29 super(SelectDateWidget, self).__init__(attrs, row_attrs) 30 30 if years: 31 31 self.years = years 32 32 else: -
django/forms/forms.py
=== modified file 'django/forms/forms.py'
4 4 5 5 from copy import deepcopy 6 6 7 from django.utils.datastructures import SortedDict 8 from django.utils.html import escape 7 from django.utils.html import conditional_escape 9 8 from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode 10 9 from django.utils.safestring import mark_safe 11 10 12 from fields import Fi eld, FileField13 from widgets import Media, media_property,TextInput, Textarea11 from fields import FileField 12 from widgets import Media, TextInput, Textarea 14 13 from util import flatatt, ErrorDict, ErrorList, ValidationError 14 import metaclassing 15 15 16 16 __all__ = ('BaseForm', 'Form') 17 17 … … 22 22 name = name[0].upper() + name[1:] 23 23 return name.replace('_', ' ') 24 24 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 """ 25 class 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 31 class FormMetaclass(type): 58 32 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) 64 39 return new_class 65 40 66 41 class BaseForm(StrAndUnicode): … … 106 81 return BoundField(self, field, name) 107 82 108 83 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." 110 85 if self._errors is None: 111 86 self.full_clean() 112 87 return self._errors … … 119 94 """ 120 95 return self.is_bound and not bool(self.errors) 121 96 122 def add_prefix(self, field_name):97 def add_prefix(self, name): 123 98 """ 124 99 Returns the field name with a prefix appended, if this Form has a 125 100 prefix set. 126 101 127 102 Subclasses may wish to override. 128 103 """ 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): 132 191 "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(), [], {} 135 194 for name, field in self.fields.items(): 136 195 bf = BoundField(self, field, name) 137 bf_errors = self.error_class([escape(error) for error in bf.errors]) # Escape and cache in local variable.138 196 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]) 141 199 hidden_fields.append(unicode(bf)) 142 200 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) 160 202 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)) 173 217 return mark_safe(u'\n'.join(output)) 174 218 175 219 def as_table(self): 176 220 "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) 178 232 179 233 def as_ul(self): 180 234 "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) 182 246 183 247 def as_p(self): 184 248 "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) 186 260 187 261 def non_field_errors(self): 188 262 """ … … 221 295 value = getattr(self, 'clean_%s' % name)() 222 296 self.cleaned_data[name] = value 223 297 except ValidationError, e: 224 self._errors[name] = e.messages298 self._errors[name] = self.error_class(e.messages) 225 299 if name in self.cleaned_data: 226 300 del self.cleaned_data[name] 227 301 try: 228 302 self.cleaned_data = self.clean() 229 303 except ValidationError, e: 230 self._errors[NON_FIELD_ERRORS] = e.messages304 self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages) 231 305 if self._errors: 232 306 delattr(self, 'cleaned_data') 233 307 … … 291 365 # fancy metaclass stuff purely for the semantic sugar -- it allows one 292 366 # to define a form using declarative syntax. 293 367 # BaseForm itself has no way of designating self.fields. 294 __metaclass__ = DeclarativeFieldsMetaclass 368 __metaclass__ = FormMetaclass 369 _options = FormOptions 295 370 296 371 class BoundField(StrAndUnicode): 297 372 "A Field plus data" 298 373 def __init__(self, form, field, name): 299 374 self.form = form 300 375 self.field = field 376 self.widget = field.widget 377 self.required = self.field.required 378 self.is_hidden = self.widget.is_hidden 301 379 self.name = name 302 380 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.label307 self.help_text = field.help_text or ''308 381 309 382 def __unicode__(self): 310 383 """Renders this field as an HTML widget.""" … … 325 398 field's default widget will be used. 326 399 """ 327 400 if not widget: 328 widget = self. field.widget401 widget = self.widget 329 402 attrs = attrs or {} 330 403 auto_id = self.auto_id 331 404 if auto_id and 'id' not in attrs and 'id' not in widget.attrs: … … 358 431 """ 359 432 Returns the data for this BoundField, or None if it wasn't given. 360 433 """ 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) 362 435 data = property(_data) 363 436 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 364 465 def label_tag(self, contents=None, attrs=None): 365 466 """ 366 467 Wraps the given contents in a <label>, if the field has an ID attribute. … … 369 470 370 471 If attrs are given, they're used as HTML attributes on the <label> tag. 371 472 """ 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: 376 475 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) 378 477 return mark_safe(contents) 379 478 380 def _is_hidden(self):381 "Returns True if this BoundField's widget is hidden."382 return self.field.widget.is_hidden383 is_hidden = property(_is_hidden)384 385 479 def _auto_id(self): 386 480 """ 387 481 Calculates and returns the ID attribute for this BoundField, if the -
django/forms/models.py
=== modified file 'django/forms/models.py'
8 8 from django.utils.datastructures import SortedDict 9 9 10 10 from util import ValidationError, ErrorList 11 from forms import BaseForm, get_declared_fields11 from forms import FormOptions, FormMetaclass, BaseForm 12 12 from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES 13 13 from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput 14 from widgets import media_property15 14 from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME 15 import metaclassing 16 16 17 17 __all__ = ( 18 18 'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model', … … 147 147 field_list.append((f.name, formfield)) 148 148 return SortedDict(field_list) 149 149 150 class ModelFormOptions( object):150 class ModelFormOptions(FormOptions): 151 151 def __init__(self, options=None): 152 super(ModelFormOptions, self).__init__(options) 152 153 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 155 class ModelFormMetaclass(FormMetaclass): 158 156 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) 186 164 return new_class 187 165 188 166 class BaseModelForm(BaseForm): … … 196 174 object_data = {} 197 175 else: 198 176 self.instance = instance 199 object_data = model_to_dict(instance , opts.fields, opts.exclude)177 object_data = model_to_dict(instance) 200 178 # if initial was provided, it should override the values from instance 201 179 if initial is not None: 202 180 object_data.update(initial) … … 215 193 fail_message = 'created' 216 194 else: 217 195 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) 219 197 220 198 class ModelForm(BaseModelForm): 221 199 __metaclass__ = ModelFormMetaclass 200 _options = ModelFormOptions 222 201 223 202 def modelform_factory(model, form=ModelForm, fields=None, exclude=None, 224 203 formfield_callback=lambda f: f.formfield()): -
django/forms/util.py
=== modified file 'django/forms/util.py'
39 39 def as_ul(self): 40 40 if not self: return u'' 41 41 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])) 43 43 44 44 def as_text(self): 45 45 if not self: return u'' -
django/forms/widgets.py
=== modified file 'django/forms/widgets.py'
133 133 is_hidden = False # Determines whether this corresponds to an <input type="hidden">. 134 134 needs_multipart_form = False # Determines does this widget need multipart-encrypted form 135 135 136 def __init__(self, attrs=None ):136 def __init__(self, attrs=None, row_attrs=None): 137 137 if attrs is not None: 138 138 self.attrs = attrs.copy() 139 139 else: 140 140 self.attrs = {} 141 if row_attrs is not None: 142 self.row_attrs = row_attrs.copy() 143 else: 144 self.row_attrs = {} 141 145 142 146 def __deepcopy__(self, memo): 143 147 obj = copy.copy(self) 144 148 obj.attrs = self.attrs.copy() 149 obj.row_attrs = self.row_attrs.copy() 145 150 memo[id(self)] = obj 146 151 return obj 147 152 … … 221 226 class PasswordInput(Input): 222 227 input_type = 'password' 223 228 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) 226 231 self.render_value = render_value 227 232 228 233 def render(self, name, value, attrs=None): … … 238 243 A widget that handles <input type="hidden"> for fields that have a list 239 244 of values. 240 245 """ 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) 243 248 # choices can be any iterable 244 249 self.choices = choices 245 250 … … 272 277 return True 273 278 274 279 class Textarea(Widget): 275 def __init__(self, attrs=None ):280 def __init__(self, attrs=None, row_attrs=None): 276 281 # The 'rows' and 'cols' attributes are required for HTML correctness. 277 self.attrs = {'cols': '40', 'rows': '10'}282 default_attrs = {'cols': '40', 'rows': '10'} 278 283 if attrs: 279 self.attrs.update(attrs) 284 default_attrs.update(attrs) 285 super(Textarea, self).__init__(default_attrs, row_attrs) 280 286 281 287 def render(self, name, value, attrs=None): 282 288 if value is None: value = '' … … 289 295 input_type = 'text' 290 296 format = '%Y-%m-%d %H:%M:%S' # '2006-10-25 14:30:59' 291 297 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) 294 300 if format: 295 301 self.format = format 296 302 … … 313 319 return super(TimeInput, self).render(name, value, attrs) 314 320 315 321 class 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) 318 324 # check_test is a callable that takes a value and returns True 319 325 # if the checkbox should be checked for that value. 320 326 self.check_test = check_test … … 345 351 return bool(initial) != bool(data) 346 352 347 353 class 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) 350 356 # choices can be any iterable, but we may need to render this widget 351 357 # multiple times. Thus, collapse it into a list so it can be consumed 352 358 # more than once. … … 386 392 """ 387 393 A Select Widget intended to be used with NullBooleanField. 388 394 """ 389 def __init__(self, attrs=None ):395 def __init__(self, attrs=None, row_attrs=None): 390 396 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) 392 398 393 399 def render(self, name, value, attrs=None, choices=()): 394 400 try: … … 581 587 582 588 You'll probably want to use this class with MultiValueField. 583 589 """ 584 def __init__(self, widgets, attrs=None ):590 def __init__(self, widgets, attrs=None, row_attrs=None): 585 591 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) 587 593 588 594 def render(self, name, value, attrs=None): 589 595 # value is a list of values, each corresponding to a widget … … 653 659 """ 654 660 A Widget that splits datetime input into two <input type="text"> boxes. 655 661 """ 656 def __init__(self, attrs=None ):662 def __init__(self, attrs=None, row_attrs=None): 657 663 widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs)) 658 super(SplitDateTimeWidget, self).__init__(widgets, attrs )664 super(SplitDateTimeWidget, self).__init__(widgets, attrs, row_attrs) 659 665 660 666 def decompress(self, value): 661 667 if value: -
tests/modeltests/generic_relations/models.py
=== modified file 'tests/modeltests/generic_relations/models.py'
214 214 >>> for form in formset.forms: 215 215 ... print form.as_p() 216 216 <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> 218 219 219 220 >>> formset = GenericFormSet(instance=platypus) 220 221 >>> for form in formset.forms: 221 222 ... print form.as_p() 222 223 <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> 224 226 <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> 226 229 227 230 """} -
tests/modeltests/model_forms/models.py
=== modified file 'tests/modeltests/model_forms/models.py'
175 175 ... model = Category 176 176 ... fields = ['name', 'url'] 177 177 ... exclude = ['url'] 178 179 >>> CategoryForm.base_fields.keys() 180 ['name'] 178 Traceback (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__) 187 ImproperlyConfigured: CategoryForm cannot have more than one option from fieldsets, fields and exclude. 181 188 182 189 Don't allow more than one 'model' definition in the inheritance hierarchy. 183 190 Technically, 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'
72 72 >>> formset = AuthorFormSet(queryset=qs) 73 73 >>> for form in formset.forms: 74 74 ... 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> 78 81 79 82 >>> data = { 80 83 ... 'form-TOTAL_FORMS': '3', # the number of forms rendered … … 108 111 >>> formset = AuthorFormSet(queryset=qs) 109 112 >>> for form in formset.forms: 110 113 ... 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> 114 120 115 121 116 122 >>> data = { … … 148 154 >>> for form in formset.forms: 149 155 ... print form.as_p() 150 156 <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> 152 159 <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> 154 162 <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> 156 165 <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> 158 168 159 169 >>> data = { 160 170 ... 'form-TOTAL_FORMS': '4', # the number of forms rendered … … 262 272 >>> for form in formset.forms: 263 273 ... print form.as_p() 264 274 <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> 266 277 267 278 >>> data = { 268 279 ... 'form-TOTAL_FORMS': '1', # the number of forms rendered … … 283 294 >>> for form in formset.forms: 284 295 ... print form.as_p() 285 296 <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> 287 299 <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> 289 302 290 303 >>> data = { 291 304 ... 'form-TOTAL_FORMS': '2', # the number of forms rendered … … 317 330 >>> formset = AuthorBooksFormSet(instance=author) 318 331 >>> for form in formset.forms: 319 332 ... 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> 323 339 324 340 >>> data = { 325 341 ... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered … … 351 367 >>> formset = AuthorBooksFormSet(instance=author) 352 368 >>> for form in formset.forms: 353 369 ... 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> 357 376 358 377 >>> data = { 359 378 ... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered … … 405 424 >>> formset = AuthorBooksFormSet(prefix="test") 406 425 >>> for form in formset.forms: 407 426 ... 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> 410 431 411 432 # Test a custom primary key ################################################### 412 433 -
tests/regressiontests/forms/extra.py
=== modified file 'tests/regressiontests/forms/extra.py'
439 439 >>> f = CommentForm(data, auto_id=False, error_class=DivErrorList) 440 440 >>> print f.as_p() 441 441 <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> 446 444 447 445 ################################# 448 446 # Test multipart-encoded form # -
tests/regressiontests/forms/fields.py
=== modified file 'tests/regressiontests/forms/fields.py'
1097 1097 ... hidden_nullbool2 = NullBooleanField(widget=HiddenInput, initial=False) 1098 1098 >>> f = HiddenNullBooleanForm() 1099 1099 >>> 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> 1101 1101 >>> f = HiddenNullBooleanForm({ 'hidden_nullbool1': 'True', 'hidden_nullbool2': 'False' }) 1102 1102 >>> f.full_clean() 1103 1103 >>> f.cleaned_data['hidden_nullbool1'] -
tests/regressiontests/forms/forms.py
=== modified file 'tests/regressiontests/forms/forms.py'
94 94 <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> 95 95 <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> 96 96 >>> 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> 103 100 104 101 If you don't pass any values to the Form's __init__(), or if you pass None, 105 102 the Form will be considered unbound and won't do any validation. Form.errors … … 569 566 ... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=MultipleHiddenInput) 570 567 >>> f = SongFormHidden(MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])), auto_id=False) 571 568 >>> 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" /> 573 571 <input type="hidden" name="composers" value="P" /></li> 574 572 575 573 When using CheckboxSelectMultiple, the framework expects a list of input and … … 814 812 >>> print p 815 813 <tr><th>First name:</th><td><input type="text" name="first_name" /></td></tr> 816 814 <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> 818 817 >>> print p.as_ul() 819 818 <li>First name: <input type="text" name="first_name" /></li> 820 819 <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> 822 822 >>> print p.as_p() 823 823 <p>First name: <input type="text" name="first_name" /></p> 824 824 <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> 826 827 827 828 With auto_id set, a HiddenInput still gets an ID, but it doesn't get a label. 828 829 >>> p = Person(auto_id='id_%s') 829 830 >>> print p 830 831 <tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr> 831 832 <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> 833 835 >>> print p.as_ul() 834 836 <li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li> 835 837 <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> 837 840 >>> print p.as_p() 838 841 <p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p> 839 842 <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> 841 845 842 846 If a field with a HiddenInput has errors, the as_table() and as_ul() output 843 847 will include the error message(s) with the text "(Hidden field [fieldname]) " … … 848 852 <tr><td colspan="2"><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></td></tr> 849 853 <tr><th>First name:</th><td><input type="text" name="first_name" value="John" /></td></tr> 850 854 <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> 852 857 >>> print p.as_ul() 853 858 <li><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></li> 854 859 <li>First name: <input type="text" name="first_name" value="John" /></li> 855 860 <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> 857 863 >>> print p.as_p() 858 864 <ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul> 859 865 <p>First name: <input type="text" name="first_name" value="John" /></p> 860 866 <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> 862 869 863 870 A corner case: It's possible for a form to have only HiddenInputs. 864 871 >>> class TestForm(Form): … … 866 873 ... bar = CharField(widget=HiddenInput) 867 874 >>> p = TestForm(auto_id=False) 868 875 >>> 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> 870 877 >>> 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> 872 879 >>> 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> 874 881 875 882 A Form's fields are displayed in the same order in which they were defined. 876 883 >>> class TestForm(Form): … … 1221 1228 >>> p = UserRegistration(auto_id=False) 1222 1229 >>> print p.as_ul() 1223 1230 <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> 1225 1233 1226 1234 Help text can include arbitrary Unicode characters. 1227 1235 >>> class UserRegistration(Form): … … 1265 1273 ... haircut_type = CharField() 1266 1274 >>> b = Beatle(auto_id=False) 1267 1275 >>> print b.as_ul() 1276 <li>Instrument: <input type="text" name="instrument" /></li> 1268 1277 <li>First name: <input type="text" name="first_name" /></li> 1269 1278 <li>Last name: <input type="text" name="last_name" /></li> 1270 1279 <li>Birthday: <input type="text" name="birthday" /></li> 1271 <li>Instrument: <input type="text" name="instrument" /></li>1272 1280 <li>Haircut type: <input type="text" name="haircut_type" /></li> 1273 1281 1274 1282 # Forms with prefixes ######################################################### -
tests/regressiontests/forms/formsets.py
=== modified file 'tests/regressiontests/forms/formsets.py'
20 20 21 21 >>> formset = ChoiceFormSet(auto_id=False, prefix='choices') 22 22 >>> 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> 24 24 <tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr> 25 25 <tr><th>Votes:</th><td><input type="text" name="choices-0-votes" /></td></tr> 26 26 -
tests/regressiontests/forms/regressions.py
=== modified file 'tests/regressiontests/forms/regressions.py'
56 56 >>> activate('ru') 57 57 >>> f = SomeForm({}) 58 58 >>> 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>'59 u'<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>' 60 60 >>> deactivate() 61 61 62 62 Deep copying translated text shouldn't raise an error -
tests/regressiontests/forms/util.py
=== modified file 'tests/regressiontests/forms/util.py'
49 49 # Can take a non-string. 50 50 >>> print ValidationError(VeryBadError()).messages 51 51 <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: <a href="http://www.example.com/">example</a></li></ul> 60 >>> print ValidationError(escape(example)).messages 61 <ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></li></ul> 62 >>> print ValidationError(conditional_escape(example)).messages 63 <ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></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: <a href="http://www.example.com/">example</a></li></ul> 69 >>> print conditional_escape(unicode(ValidationError(escape(example)).messages)) 70 <ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></li></ul> 71 >>> print conditional_escape(unicode(ValidationError(conditional_escape(example)).messages)) 72 <ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></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> 52 75 """