Ticket #6630: 00-forms-fieldsets.diff
File 00-forms-fieldsets.diff, 63.6 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/admin/validation.py
=== modified file 'django/contrib/admin/validation.py'
263 263 % (cls.__name__, label, field, model.__name__)) 264 264 265 265 def _check_form_field_exists(cls, model, opts, label, field): 266 if hasattr(cls.form, 'base_fields') :266 if hasattr(cls.form, 'base_fields') and cls.form.base_fields: 267 267 try: 268 268 cls.form.base_fields[field] 269 269 except KeyError: -
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/forms.py
=== modified file 'django/contrib/auth/tests/forms.py'
42 42 >>> form.is_valid() 43 43 False 44 44 >>> form["password2"].errors 45 [u "The two password fields didn't match."]45 [u'The two password fields didn't match.'] 46 46 47 47 The success case. 48 48 … … 107 107 >>> form.is_valid() 108 108 False 109 109 >>> form["new_password2"].errors 110 [u "The two password fields didn't match."]110 [u'The two password fields didn't match.'] 111 111 112 112 The success case. 113 113 … … 145 145 >>> form.is_valid() 146 146 False 147 147 >>> form["new_password2"].errors 148 [u "The two password fields didn't match."]148 [u'The two password fields didn't match.'] 149 149 150 150 The success case. 151 151 -
django/contrib/auth/tests/views.py
=== modified file 'django/contrib/auth/tests/views.py'
13 13 response = self.client.get('/password_reset/') 14 14 self.assertEquals(response.status_code, 200) 15 15 response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'}) 16 self.assertContains(response, "That e-mail address doesn 't have an associated user account")16 self.assertContains(response, "That e-mail address doesn't have an associated user account") 17 17 self.assertEquals(len(mail.outbox), 0) 18 18 19 19 def test_email_found(self): … … 84 84 response = self.client.post(path, {'new_password1': 'anewpassword', 85 85 'new_password2':' x'}) 86 86 self.assertEquals(response.status_code, 200) 87 self.assert_("The two password fields didn 't match" in response.content)87 self.assert_("The two password fields didn't match" in response.content) 88 88 -
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'
10 10 from django.utils.datastructures import SortedDict 11 11 12 12 from util import ValidationError, ErrorList 13 from forms import BaseForm, get_declared_fields13 from forms import FormOptions, FormMetaclass, BaseForm 14 14 from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES 15 15 from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput 16 from widgets import media_property17 16 from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME 17 import metaclassing 18 18 19 19 __all__ = ( 20 20 'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model', … … 206 206 field_list.append((f.name, formfield)) 207 207 return SortedDict(field_list) 208 208 209 class ModelFormOptions( object):209 class ModelFormOptions(FormOptions): 210 210 def __init__(self, options=None): 211 super(ModelFormOptions, self).__init__(options) 211 212 self.model = getattr(options, 'model', None) 212 self.fields = getattr(options, 'fields', None) 213 self.exclude = getattr(options, 'exclude', None) 214 215 216 class ModelFormMetaclass(type): 213 214 class ModelFormMetaclass(FormMetaclass): 217 215 def __new__(cls, name, bases, attrs): 218 formfield_callback = attrs.pop('formfield_callback', 219 lambda f: f.formfield()) 220 try: 221 parents = [b for b in bases if issubclass(b, ModelForm)] 222 except NameError: 223 # We are defining ModelForm itself. 224 parents = None 225 new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases, 226 attrs) 227 if not parents: 228 return new_class 229 230 if 'media' not in attrs: 231 new_class.media = media_property(new_class) 232 declared_fields = get_declared_fields(bases, attrs, False) 233 opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) 234 if opts.model: 235 # If a model is defined, extract form fields from it. 236 fields = fields_for_model(opts.model, opts.fields, 237 opts.exclude, formfield_callback) 238 # Override default model fields with any custom declared ones 239 # (plus, include all the other declared fields). 240 fields.update(declared_fields) 241 else: 242 fields = declared_fields 243 new_class.declared_fields = declared_fields 244 new_class.base_fields = fields 216 new_class = type.__new__(cls, name, bases, attrs) 217 metaclassing.create_meta(new_class, attrs) 218 metaclassing.create_model_fields(new_class, attrs) 219 metaclassing.create_declared_fields(new_class, attrs) 220 metaclassing.create_base_fields_pool_from_model_fields_and_declared_fields(new_class, attrs) 221 metaclassing.create_base_fields_from_base_fields_pool(new_class, attrs) 222 metaclassing.create_media(new_class, attrs) 245 223 return new_class 246 224 247 225 class BaseModelForm(BaseForm): … … 255 233 object_data = {} 256 234 else: 257 235 self.instance = instance 258 object_data = model_to_dict(instance, opts.fields, opts.exclude)236 object_data = model_to_dict(instance, self.base_fields.keys()) 259 237 # if initial was provided, it should override the values from instance 260 238 if initial is not None: 261 239 object_data.update(initial) … … 274 252 fail_message = 'created' 275 253 else: 276 254 fail_message = 'changed' 277 return save_instance(self, self.instance, self. _meta.fields, fail_message, commit)255 return save_instance(self, self.instance, self.base_fields.keys(), fail_message, commit) 278 256 279 257 class ModelForm(BaseModelForm): 280 258 __metaclass__ = ModelFormMetaclass 259 _options = ModelFormOptions 281 260 282 261 def modelform_factory(model, form=ModelForm, fields=None, exclude=None, 283 262 formfield_callback=lambda f: f.formfield()): -
django/forms/util.py
=== modified file 'django/forms/util.py'
1 from django.utils.html import escape1 from django.utils.html import conditional_escape 2 2 from django.utils.encoding import smart_unicode, StrAndUnicode, force_unicode 3 3 from django.utils.safestring import mark_safe 4 4 … … 9 9 XML-style pairs. It is assumed that the keys do not need to be XML-escaped. 10 10 If the passed dictionary is empty, then return an empty string. 11 11 """ 12 return u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])12 return mark_safe(u''.join([u' %s="%s"' % (k, conditional_escape(v)) for k, v in attrs.items()])) 13 13 14 14 class ErrorDict(dict, StrAndUnicode): 15 15 """ … … 55 55 a string) or a list of objects. 56 56 """ 57 57 if isinstance(message, list): 58 self.messages = ErrorList([ smart_unicode(msg) for msg in message])58 self.messages = ErrorList([conditional_escape(smart_unicode(msg)) for msg in message]) 59 59 else: 60 message = smart_unicode(message)60 message = conditional_escape(smart_unicode(message)) 61 61 self.messages = ErrorList([message]) 62 62 63 63 def __str__(self): -
django/forms/widgets.py
=== modified file 'django/forms/widgets.py'
132 132 is_hidden = False # Determines whether this corresponds to an <input type="hidden">. 133 133 needs_multipart_form = False # Determines does this widget need multipart-encrypted form 134 134 135 def __init__(self, attrs=None ):135 def __init__(self, attrs=None, row_attrs=None): 136 136 if attrs is not None: 137 137 self.attrs = attrs.copy() 138 138 else: 139 139 self.attrs = {} 140 if row_attrs is not None: 141 self.row_attrs = row_attrs.copy() 142 else: 143 self.row_attrs = {} 140 144 141 145 def __deepcopy__(self, memo): 142 146 obj = copy.copy(self) 143 147 obj.attrs = self.attrs.copy() 148 obj.row_attrs = self.row_attrs.copy() 144 149 memo[id(self)] = obj 145 150 return obj 146 151 … … 220 225 class PasswordInput(Input): 221 226 input_type = 'password' 222 227 223 def __init__(self, attrs=None, render_value=True ):224 super(PasswordInput, self).__init__(attrs )228 def __init__(self, attrs=None, render_value=True, row_attrs=None): 229 super(PasswordInput, self).__init__(attrs, row_attrs) 225 230 self.render_value = render_value 226 231 227 232 def render(self, name, value, attrs=None): … … 237 242 A widget that handles <input type="hidden"> for fields that have a list 238 243 of values. 239 244 """ 240 def __init__(self, attrs=None, choices=() ):241 super(MultipleHiddenInput, self).__init__(attrs )245 def __init__(self, attrs=None, choices=(), row_attrs=None): 246 super(MultipleHiddenInput, self).__init__(attrs, row_attrs) 242 247 # choices can be any iterable 243 248 self.choices = choices 244 249 … … 271 276 return True 272 277 273 278 class Textarea(Widget): 274 def __init__(self, attrs=None ):279 def __init__(self, attrs=None, row_attrs=None): 275 280 # The 'rows' and 'cols' attributes are required for HTML correctness. 276 self.attrs = {'cols': '40', 'rows': '10'}281 default_attrs = {'cols': '40', 'rows': '10'} 277 282 if attrs: 278 self.attrs.update(attrs) 283 default_attrs.update(attrs) 284 super(Textarea, self).__init__(default_attrs, row_attrs) 279 285 280 286 def render(self, name, value, attrs=None): 281 287 if value is None: value = '' … … 288 294 input_type = 'text' 289 295 format = '%Y-%m-%d %H:%M:%S' # '2006-10-25 14:30:59' 290 296 291 def __init__(self, attrs=None, format=None ):292 super(DateTimeInput, self).__init__(attrs )297 def __init__(self, attrs=None, format=None, row_attrs=None): 298 super(DateTimeInput, self).__init__(attrs, row_attrs) 293 299 if format: 294 300 self.format = format 295 301 … … 302 308 return super(DateTimeInput, self).render(name, value, attrs) 303 309 304 310 class CheckboxInput(Widget): 305 def __init__(self, attrs=None, check_test=bool ):306 super(CheckboxInput, self).__init__(attrs )311 def __init__(self, attrs=None, check_test=bool, row_attrs=None): 312 super(CheckboxInput, self).__init__(attrs, row_attrs) 307 313 # check_test is a callable that takes a value and returns True 308 314 # if the checkbox should be checked for that value. 309 315 self.check_test = check_test … … 334 340 return bool(initial) != bool(data) 335 341 336 342 class Select(Widget): 337 def __init__(self, attrs=None, choices=() ):338 super(Select, self).__init__(attrs )343 def __init__(self, attrs=None, choices=(), row_attrs=None): 344 super(Select, self).__init__(attrs, row_attrs) 339 345 # choices can be any iterable, but we may need to render this widget 340 346 # multiple times. Thus, collapse it into a list so it can be consumed 341 347 # more than once. … … 375 381 """ 376 382 A Select Widget intended to be used with NullBooleanField. 377 383 """ 378 def __init__(self, attrs=None ):384 def __init__(self, attrs=None, row_attrs=None): 379 385 choices = ((u'1', ugettext('Unknown')), (u'2', ugettext('Yes')), (u'3', ugettext('No'))) 380 super(NullBooleanSelect, self).__init__(attrs, choices )386 super(NullBooleanSelect, self).__init__(attrs, choices, row_attrs) 381 387 382 388 def render(self, name, value, attrs=None, choices=()): 383 389 try: … … 570 576 571 577 You'll probably want to use this class with MultiValueField. 572 578 """ 573 def __init__(self, widgets, attrs=None ):579 def __init__(self, widgets, attrs=None, row_attrs=None): 574 580 self.widgets = [isinstance(w, type) and w() or w for w in widgets] 575 super(MultiWidget, self).__init__(attrs )581 super(MultiWidget, self).__init__(attrs, row_attrs) 576 582 577 583 def render(self, name, value, attrs=None): 578 584 # value is a list of values, each corresponding to a widget … … 642 648 """ 643 649 A Widget that splits datetime input into two <input type="text"> boxes. 644 650 """ 645 def __init__(self, attrs=None ):651 def __init__(self, attrs=None, row_attrs=None): 646 652 widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs)) 647 super(SplitDateTimeWidget, self).__init__(widgets, attrs )653 super(SplitDateTimeWidget, self).__init__(widgets, attrs, row_attrs) 648 654 649 655 def decompress(self, value): 650 656 if value: -
tests/modeltests/model_forms/models.py
=== modified file 'tests/modeltests/model_forms/models.py'
151 151 ... model = Category 152 152 ... fields = ['name', 'url'] 153 153 ... exclude = ['url'] 154 155 >>> CategoryForm.base_fields.keys() 156 ['name'] 154 Traceback (most recent call last): 155 File "/home/petr/django/local2/00-forms-fieldsets/django/test/_doctest.py", line 1267, in __run 156 compileflags, 1) in test.globs 157 File "<doctest modeltests.model_forms.models.__test__.API_TESTS[12]>", line 1, in ? 158 class CategoryForm(ModelForm): 159 File "/home/petr/django/local2/00-forms-fieldsets/django/forms/models.py", line 220, in __new__ 160 metaclassing.create_base_fields_from_base_fields_pool(new_class) 161 File "/home/petr/django/local2/00-forms-fieldsets/django/forms/metaclassing.py", line 50, in create_base_fields_from_base_fields_pool 162 raise ImproperlyConfigured("%s cannot have more than one option from fieldsets, fields and exclude." % cls.__name__) 163 ImproperlyConfigured: CategoryForm cannot have more than one option from fieldsets, fields and exclude. 157 164 158 165 Don't allow more than one 'model' definition in the inheritance hierarchy. 159 166 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'
46 46 >>> formset = AuthorFormSet(queryset=qs) 47 47 >>> for form in formset.forms: 48 48 ... print form.as_p() 49 <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> 50 <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> 51 <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> 49 <p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></p> 50 <p class ="hidden"><input type="hidden" name="form-0-id" id="id_form-0-id" /></p> 51 <p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" maxlength="100" /></p> 52 <p class ="hidden"><input type="hidden" name="form-1-id" id="id_form-1-id" /></p> 53 <p><label for="id_form-2-name">Name:</label> <input id="id_form-2-name" type="text" name="form-2-name" maxlength="100" /></p> 54 <p class ="hidden"><input type="hidden" name="form-2-id" id="id_form-2-id" /></p> 52 55 53 56 >>> data = { 54 57 ... 'form-TOTAL_FORMS': '3', # the number of forms rendered … … 82 85 >>> formset = AuthorFormSet(queryset=qs) 83 86 >>> for form in formset.forms: 84 87 ... print form.as_p() 85 <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> 86 <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> 87 <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> 88 <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> 89 <p class ="hidden"><input type="hidden" name="form-0-id" value="2" id="id_form-0-id" /></p> 90 <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> 91 <p class ="hidden"><input type="hidden" name="form-1-id" value="1" id="id_form-1-id" /></p> 92 <p><label for="id_form-2-name">Name:</label> <input id="id_form-2-name" type="text" name="form-2-name" maxlength="100" /></p> 93 <p class ="hidden"><input type="hidden" name="form-2-id" id="id_form-2-id" /></p> 88 94 89 95 90 96 >>> data = { … … 122 128 >>> for form in formset.forms: 123 129 ... print form.as_p() 124 130 <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> 125 <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> 131 <p><label for="id_form-0-DELETE">Delete:</label> <input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /></p> 132 <p class ="hidden"><input type="hidden" name="form-0-id" value="2" id="id_form-0-id" /></p> 126 133 <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> 127 <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> 134 <p><label for="id_form-1-DELETE">Delete:</label> <input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE" /></p> 135 <p class ="hidden"><input type="hidden" name="form-1-id" value="1" id="id_form-1-id" /></p> 128 136 <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> 129 <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> 137 <p><label for="id_form-2-DELETE">Delete:</label> <input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE" /></p> 138 <p class ="hidden"><input type="hidden" name="form-2-id" value="3" id="id_form-2-id" /></p> 130 139 <p><label for="id_form-3-name">Name:</label> <input id="id_form-3-name" type="text" name="form-3-name" maxlength="100" /></p> 131 <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> 140 <p><label for="id_form-3-DELETE">Delete:</label> <input type="checkbox" name="form-3-DELETE" id="id_form-3-DELETE" /></p> 141 <p class ="hidden"><input type="hidden" name="form-3-id" id="id_form-3-id" /></p> 132 142 133 143 >>> data = { 134 144 ... 'form-TOTAL_FORMS': '4', # the number of forms rendered … … 243 253 >>> formset = AuthorBooksFormSet(instance=author) 244 254 >>> for form in formset.forms: 245 255 ... print form.as_p() 246 <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> 247 <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> 248 <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> 256 <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> 257 <p class ="hidden"><input type="hidden" name="book_set-0-id" id="id_book_set-0-id" /></p> 258 <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> 259 <p class ="hidden"><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p> 260 <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> 261 <p class ="hidden"><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p> 249 262 250 263 >>> data = { 251 264 ... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered … … 277 290 >>> formset = AuthorBooksFormSet(instance=author) 278 291 >>> for form in formset.forms: 279 292 ... print form.as_p() 280 <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> 281 <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> 282 <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> 293 <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> 294 <p class ="hidden"><input type="hidden" name="book_set-0-id" value="1" id="id_book_set-0-id" /></p> 295 <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> 296 <p class ="hidden"><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p> 297 <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> 298 <p class ="hidden"><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p> 283 299 284 300 >>> data = { 285 301 ... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered … … 331 347 >>> formset = AuthorBooksFormSet(prefix="test") 332 348 >>> for form in formset.forms: 333 349 ... print form.as_p() 334 <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> 335 <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> 350 <p><label for="id_test-0-title">Title:</label> <input id="id_test-0-title" type="text" name="test-0-title" maxlength="100" /></p> 351 <p class ="hidden"><input type="hidden" name="test-0-id" id="id_test-0-id" /></p> 352 <p><label for="id_test-1-title">Title:</label> <input id="id_test-1-title" type="text" name="test-1-title" maxlength="100" /></p> 353 <p class ="hidden"><input type="hidden" name="test-1-id" id="id_test-1-id" /></p> 336 354 337 355 # Test a custom primary key ################################################### 338 356 -
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/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 … … 563 560 ... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=MultipleHiddenInput) 564 561 >>> f = SongFormHidden(MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])), auto_id=False) 565 562 >>> print f.as_ul() 566 <li>Name: <input type="text" name="name" value="Yesterday" /><input type="hidden" name="composers" value="J" /> 563 <li>Name: <input type="text" name="name" value="Yesterday" /></li> 564 <li class ="hidden"><input type="hidden" name="composers" value="J" /> 567 565 <input type="hidden" name="composers" value="P" /></li> 568 566 569 567 When using CheckboxSelectMultiple, the framework expects a list of input and … … 808 806 >>> print p 809 807 <tr><th>First name:</th><td><input type="text" name="first_name" /></td></tr> 810 808 <tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr> 811 <tr><th>Birthday:</th><td><input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></td></tr> 809 <tr><th>Birthday:</th><td><input type="text" name="birthday" /></td></tr> 810 <tr class="hidden"><td colspan="2"><input type="hidden" name="hidden_text" /></td></tr> 812 811 >>> print p.as_ul() 813 812 <li>First name: <input type="text" name="first_name" /></li> 814 813 <li>Last name: <input type="text" name="last_name" /></li> 815 <li>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></li> 814 <li>Birthday: <input type="text" name="birthday" /></li> 815 <li class ="hidden"><input type="hidden" name="hidden_text" /></li> 816 816 >>> print p.as_p() 817 817 <p>First name: <input type="text" name="first_name" /></p> 818 818 <p>Last name: <input type="text" name="last_name" /></p> 819 <p>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></p> 819 <p>Birthday: <input type="text" name="birthday" /></p> 820 <p class ="hidden"><input type="hidden" name="hidden_text" /></p> 820 821 821 822 With auto_id set, a HiddenInput still gets an ID, but it doesn't get a label. 822 823 >>> p = Person(auto_id='id_%s') 823 824 >>> print p 824 825 <tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr> 825 826 <tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr> 826 <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> 827 <tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" id="id_birthday" /></td></tr> 828 <tr class="hidden"><td colspan="2"><input type="hidden" name="hidden_text" id="id_hidden_text" /></td></tr> 827 829 >>> print p.as_ul() 828 830 <li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li> 829 831 <li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li> 830 <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> 832 <li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li> 833 <li class ="hidden"><input type="hidden" name="hidden_text" id="id_hidden_text" /></li> 831 834 >>> print p.as_p() 832 835 <p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p> 833 836 <p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p> 834 <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> 837 <p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p> 838 <p class ="hidden"><input type="hidden" name="hidden_text" id="id_hidden_text" /></p> 835 839 836 840 If a field with a HiddenInput has errors, the as_table() and as_ul() output 837 841 will include the error message(s) with the text "(Hidden field [fieldname]) " … … 842 846 <tr><td colspan="2"><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></td></tr> 843 847 <tr><th>First name:</th><td><input type="text" name="first_name" value="John" /></td></tr> 844 848 <tr><th>Last name:</th><td><input type="text" name="last_name" value="Lennon" /></td></tr> 845 <tr><th>Birthday:</th><td><input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></td></tr> 849 <tr><th>Birthday:</th><td><input type="text" name="birthday" value="1940-10-9" /></td></tr> 850 <tr class="hidden"><td colspan="2"><input type="hidden" name="hidden_text" /></td></tr> 846 851 >>> print p.as_ul() 847 852 <li><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></li> 848 853 <li>First name: <input type="text" name="first_name" value="John" /></li> 849 854 <li>Last name: <input type="text" name="last_name" value="Lennon" /></li> 850 <li>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></li> 855 <li>Birthday: <input type="text" name="birthday" value="1940-10-9" /></li> 856 <li class ="hidden"><input type="hidden" name="hidden_text" /></li> 851 857 >>> print p.as_p() 852 858 <ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul> 853 859 <p>First name: <input type="text" name="first_name" value="John" /></p> 854 860 <p>Last name: <input type="text" name="last_name" value="Lennon" /></p> 855 <p>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></p> 861 <p>Birthday: <input type="text" name="birthday" value="1940-10-9" /></p> 862 <p class ="hidden"><input type="hidden" name="hidden_text" /></p> 856 863 857 864 A corner case: It's possible for a form to have only HiddenInputs. 858 865 >>> class TestForm(Form): … … 860 867 ... bar = CharField(widget=HiddenInput) 861 868 >>> p = TestForm(auto_id=False) 862 869 >>> print p.as_table() 863 < input type="hidden" name="foo" /><input type="hidden" name="bar" />870 <tr class="hidden"><td colspan="2"><input type="hidden" name="foo" /><input type="hidden" name="bar" /></td></tr> 864 871 >>> print p.as_ul() 865 < input type="hidden" name="foo" /><input type="hidden" name="bar" />872 <li class ="hidden"><input type="hidden" name="foo" /><input type="hidden" name="bar" /></li> 866 873 >>> print p.as_p() 867 < input type="hidden" name="foo" /><input type="hidden" name="bar" />874 <p class ="hidden"><input type="hidden" name="foo" /><input type="hidden" name="bar" /></p> 868 875 869 876 A Form's fields are displayed in the same order in which they were defined. 870 877 >>> class TestForm(Form): … … 1215 1222 >>> p = UserRegistration(auto_id=False) 1216 1223 >>> print p.as_ul() 1217 1224 <li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li> 1218 <li>Password: <input type="password" name="password" /><input type="hidden" name="next" value="/" /></li> 1225 <li>Password: <input type="password" name="password" /></li> 1226 <li class ="hidden"><input type="hidden" name="next" value="/" /></li> 1219 1227 1220 1228 Help text can include arbitrary Unicode characters. 1221 1229 >>> class UserRegistration(Form): … … 1259 1267 ... haircut_type = CharField() 1260 1268 >>> b = Beatle(auto_id=False) 1261 1269 >>> print b.as_ul() 1270 <li>Instrument: <input type="text" name="instrument" /></li> 1262 1271 <li>First name: <input type="text" name="first_name" /></li> 1263 1272 <li>Last name: <input type="text" name="last_name" /></li> 1264 1273 <li>Birthday: <input type="text" name="birthday" /></li> 1265 <li>Instrument: <input type="text" name="instrument" /></li>1266 1274 <li>Haircut type: <input type="text" name="haircut_type" /></li> 1267 1275 1268 1276 # 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/localflavor/ch.py
=== modified file 'tests/regressiontests/forms/localflavor/ch.py'
41 41 >>> f.clean('C1234567<1') 42 42 Traceback (most recent call last): 43 43 ... 44 ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567 <0 or 1234567890 format.']44 ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'] 45 45 >>> f.clean('2123456700') 46 46 u'2123456700' 47 47 >>> f.clean('2123456701') 48 48 Traceback (most recent call last): 49 49 ... 50 ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567 <0 or 1234567890 format.']50 ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'] 51 51 52 52 # CHStateSelect ############################################################# 53 53 -
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'
7 7 >>> from django.forms.util import * 8 8 >>> from django.utils.translation import ugettext_lazy 9 9 10 # Escaping. 11 >>> from django.utils.html import escape 12 >>> from django.utils.html import conditional_escape 13 >>> script = "$('#example').html('<a href=\"http://www.example.com/\">example</a>');" 14 10 15 ########### 11 16 # flatatt # 12 17 ########### … … 19 24 >>> flatatt({}) 20 25 u'' 21 26 27 # Escaping. 28 29 >>> flatatt({'onclick': script}) 30 u' onclick="$('#example').html('<a href="http://www.example.com/">example</a>');"' 31 >>> flatatt({'onclick': escape(script)}) 32 u' onclick="$('#example').html('<a href="http://www.example.com/">example</a>');"' 33 >>> flatatt({'onclick': conditional_escape(script)}) 34 u' onclick="$('#example').html('<a href="http://www.example.com/">example</a>');"' 35 36 >>> conditional_escape(flatatt({'onclick': script})) 37 u' onclick="$('#example').html('<a href="http://www.example.com/">example</a>');"' 38 >>> conditional_escape(flatatt({'onclick': escape(script)})) 39 u' onclick="$('#example').html('<a href="http://www.example.com/">example</a>');"' 40 >>> conditional_escape(flatatt({'onclick': conditional_escape(script)})) 41 u' onclick="$('#example').html('<a href="http://www.example.com/">example</a>');"' 42 22 43 ################### 23 44 # ValidationError # 24 45 ################### … … 49 70 # Can take a non-string. 50 71 >>> print ValidationError(VeryBadError()).messages 51 72 <ul class="errorlist"><li>A very bad error.</li></ul> 73 74 # Escaping. 75 76 >>> print ValidationError(script).messages 77 <ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul> 78 >>> print ValidationError(escape(script)).messages 79 <ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul> 80 >>> print ValidationError(conditional_escape(script)).messages 81 <ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul> 82 >>> print ErrorDict({'example': ValidationError(script).messages}) 83 <ul class="errorlist"><li>example<ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul></li></ul> 84 >>> print ErrorDict({'example': ValidationError(escape(script)).messages}) 85 <ul class="errorlist"><li>example<ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul></li></ul> 86 >>> print ErrorDict({'example': ValidationError(conditional_escape(script)).messages}) 87 <ul class="errorlist"><li>example<ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul></li></ul> 88 89 >>> print conditional_escape(unicode(ValidationError(script).messages)) 90 <ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul> 91 >>> print conditional_escape(unicode(ValidationError(escape(script)).messages)) 92 <ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul> 93 >>> print conditional_escape(unicode(ValidationError(conditional_escape(script)).messages)) 94 <ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul> 95 >>> print conditional_escape(unicode(ErrorDict({'example': ValidationError(script).messages}))) 96 <ul class="errorlist"><li>example<ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul></li></ul> 97 >>> print conditional_escape(unicode(ErrorDict({'example': ValidationError(escape(script)).messages}))) 98 <ul class="errorlist"><li>example<ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul></li></ul> 99 >>> print conditional_escape(unicode(ErrorDict({'example': ValidationError(conditional_escape(script)).messages}))) 100 <ul class="errorlist"><li>example<ul class="errorlist"><li>$('#example').html('<a href="http://www.example.com/">example</a>');</li></ul></li></ul> 101 52 102 """