Ticket #17301: ticket17301-version1.diff
File ticket17301-version1.diff, 33.2 KB (added by , 13 years ago) |
---|
-
django/forms/forms.py
diff --git a/django/forms/forms.py b/django/forms/forms.py index 1400be3..f6351c2 100644
a b def get_declared_fields(bases, attrs, with_base_fields=True): 54 54 55 55 return SortedDict(fields) 56 56 57 class DeclarativeFieldsMetaclass(type):58 """59 Metaclass that converts Field attributes to a dictionary called60 'base_fields', taking into account parent class 'base_fields' as well. 61 """ 57 class BaseFormOptions(object): 58 def __init__(self, options=None): 59 self.fieldsets = getattr(options, 'fieldsets', None) 60 61 class BaseFormMetaclass(type): 62 62 def __new__(cls, name, bases, attrs): 63 attrs['base_fields'] = get_declared_fields(bases, attrs) 64 new_class = super(DeclarativeFieldsMetaclass, 65 cls).__new__(cls, name, bases, attrs) 63 try: 64 parents = [b for b in bases if issubclass(b, BaseForm)] 65 except NameError: 66 # We are defining Form itself. 67 parents = None 68 new_class = super(BaseFormMetaclass, cls).__new__(cls, name, bases, attrs) 69 if not parents: 70 return new_class 66 71 if 'media' not in attrs: 67 72 new_class.media = media_property(new_class) 73 new_class._meta = BaseFormOptions(getattr(new_class, 'Meta', None)) 68 74 return new_class 69 75 76 class DeclarativeFieldsMetaclass(BaseFormMetaclass): 77 """ 78 Metaclass that converts Field attributes to a dictionary called 79 'base_fields', taking into account parent class 'base_fields' as well. 80 """ 81 def __new__(cls, name, bases, attrs): 82 attrs['base_fields'] = get_declared_fields(bases, attrs) 83 return super(DeclarativeFieldsMetaclass, cls).__new__(cls, name, bases, attrs) 84 70 85 class BaseForm(StrAndUnicode): 71 86 # This is the main implementation of all the Form logic. Note that this 72 87 # class is different than Form. See the comments by the Form class for more … … class BaseForm(StrAndUnicode): 138 153 """ 139 154 return u'initial-%s' % self.add_prefix(field_name) 140 155 141 def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):156 def _html_output(self, fieldset_method, error_row, before_fieldset=u'', after_fieldset=u''): 142 157 "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." 143 158 top_errors = self.non_field_errors() # Errors that should be displayed above all fields. 144 output, hidden_fields = [], [] 145 146 for name, field in self.fields.items(): 147 html_class_attr = '' 148 bf = BoundField(self, field, name) 149 bf_errors = self.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable. 150 if bf.is_hidden: 151 if bf_errors: 152 top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors]) 153 hidden_fields.append(unicode(bf)) 154 else: 155 # Create a 'class="..."' atribute if the row should have any 156 # CSS classes applied. 157 css_classes = bf.css_classes() 158 if css_classes: 159 html_class_attr = ' class="%s"' % css_classes 160 161 if errors_on_separate_row and bf_errors: 162 output.append(error_row % force_unicode(bf_errors)) 163 164 if bf.label: 165 label = conditional_escape(force_unicode(bf.label)) 166 # Only add the suffix if the label does not end in 167 # punctuation. 168 if self.label_suffix: 169 if label[-1] not in ':?.!': 170 label += self.label_suffix 171 label = bf.label_tag(label) or '' 172 else: 173 label = '' 174 175 if field.help_text: 176 help_text = help_text_html % force_unicode(field.help_text) 177 else: 178 help_text = u'' 179 180 output.append(normal_row % { 181 'errors': force_unicode(bf_errors), 182 'label': force_unicode(label), 183 'field': unicode(bf), 184 'help_text': help_text, 185 'html_class_attr': html_class_attr 186 }) 159 output = [] 160 161 for fieldset in self.fieldsets: 162 fieldset_html = [getattr(fieldset, fieldset_method)()] 163 if not fieldset.dummy: 164 fieldset_html.insert(0, u'<fieldset>') 165 fieldset_html.insert(1, before_fieldset) 166 fieldset_html.append(after_fieldset) 167 fieldset_html.append(u'</fieldset>') 168 if fieldset.legend: 169 fieldset_html.insert(1, fieldset.legend_tag()) 170 if top_errors: 171 output.insert(0, force_unicode(top_errors)) 172 output.extend(fieldset_html) 187 173 188 174 if top_errors: 189 175 output.insert(0, error_row % force_unicode(top_errors)) 190 176 191 if hidden_fields: # Insert any hidden fields in the last row.192 str_hidden = u''.join(hidden_fields)193 if output:194 last_row = output[-1]195 # Chop off the trailing row_ender (e.g. '</td></tr>') and196 # insert the hidden fields.197 if not last_row.endswith(row_ender):198 # This can happen in the as_p() case (and possibly others199 # that users write): if there are only top errors, we may200 # not be able to conscript the last row for our purposes,201 # so insert a new, empty row.202 last_row = (normal_row % {'errors': '', 'label': '',203 'field': '', 'help_text':'',204 'html_class_attr': html_class_attr})205 output.append(last_row)206 output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender207 else:208 # If there aren't any rows in the output, just append the209 # hidden fields.210 output.append(str_hidden)211 177 return mark_safe(u'\n'.join(output)) 212 178 213 179 def as_table(self): 214 180 "Returns this form rendered as HTML <tr>s -- excluding the <table></table>." 215 181 return self._html_output( 216 normal_row = u'<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>',182 fieldset_method='as_table', 217 183 error_row = u'<tr><td colspan="2">%s</td></tr>', 218 row_ender = u'</td></tr>', 219 help_text_html = u'<br /><span class="helptext">%s</span>', 220 errors_on_separate_row = False) 184 before_fieldset=u'<table>', 185 after_fieldset=u'</table>') 221 186 222 187 def as_ul(self): 223 188 "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." 224 189 return self._html_output( 225 normal_row = u'<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>',190 fieldset_method='as_ul', 226 191 error_row = u'<li>%s</li>', 227 row_ender = '</li>', 228 help_text_html = u' <span class="helptext">%s</span>', 229 errors_on_separate_row = False) 192 before_fieldset=u'<ul>', 193 after_fieldset=u'</ul>') 230 194 231 195 def as_p(self): 232 196 "Returns this form rendered as HTML <p>s." 233 197 return self._html_output( 234 normal_row = u'<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>', 235 error_row = u'%s', 236 row_ender = '</p>', 237 help_text_html = u' <span class="helptext">%s</span>', 238 errors_on_separate_row = True) 198 fieldset_method='as_p', 199 error_row = u'%s') 239 200 240 201 def non_field_errors(self): 241 202 """ … … class BaseForm(StrAndUnicode): 380 341 """ 381 342 return [field for field in self if not field.is_hidden] 382 343 344 def _fieldsets(self): 345 """ 346 Returns a list of Fieldset objects for each fieldset 347 defined in Form's Meta options. If no fieldsets were defined, 348 returns a list containing single, 'dummy' Fieldset with 349 all form fields. 350 """ 351 if self._meta.fieldsets: 352 return [Fieldset(self, legend, attrs.get('fields', tuple())) 353 for legend, attrs in self._meta.fieldsets] 354 return [Fieldset(self, None, self.fields.keys(), dummy=True)] 355 fieldsets = property(_fieldsets) 356 383 357 class Form(BaseForm): 384 358 "A collection of Fields, plus their associated data." 385 359 # This is a separate class from BaseForm in order to abstract the way … … class Form(BaseForm): 389 363 # BaseForm itself has no way of designating self.fields. 390 364 __metaclass__ = DeclarativeFieldsMetaclass 391 365 366 class Fieldset(StrAndUnicode): 367 368 def __init__(self, form, legend, fields, dummy=False): 369 """ 370 Arguments: 371 form -- form this fieldset belongs to 372 legend -- fieldset's legend (used in <legend> tag) 373 fields -- list containing names of fields in this fieldset 374 375 Keyword arguments: 376 dummy -- flag informing that the fieldset was created automatically 377 from all fields of form, because user has not defined 378 custom fieldsets 379 """ 380 self.form = form 381 self.legend = legend 382 self.fields = fields 383 self.dummy = dummy 384 385 def __unicode__(self): 386 return self.as_table() 387 388 def __iter__(self): 389 for name in self.fields: 390 yield BoundField(self.form, self.form.fields[name], name) 391 392 def __getitem__(self, name): 393 "Returns a BoundField with the given name." 394 if not name in self.fields: 395 raise KeyError('Key %r not found in Fieldset' % name) 396 return self.form[name] 397 398 def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row): 399 "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." 400 output, hidden_fields = [], [] 401 top_errors = self.form.error_class() 402 403 for name in self.fields: 404 field = self.form.fields[name] 405 html_class_attr = '' 406 bf = BoundField(self.form, field, name) 407 bf_errors = self.form.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable. 408 if bf.is_hidden: 409 if bf_errors: 410 top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors]) 411 hidden_fields.append(unicode(bf)) 412 else: 413 # Create a 'class="..."' atribute if the row should have any 414 # CSS classes applied. 415 css_classes = bf.css_classes() 416 if css_classes: 417 html_class_attr = ' class="%s"' % css_classes 418 419 if errors_on_separate_row and bf_errors: 420 output.append(error_row % force_unicode(bf_errors)) 421 422 if bf.label: 423 label = conditional_escape(force_unicode(bf.label)) 424 # Only add the suffix if the label does not end in 425 # punctuation. 426 if self.form.label_suffix: 427 if label[-1] not in ':?.!': 428 label += self.form.label_suffix 429 label = bf.label_tag(label) or '' 430 else: 431 label = '' 432 433 if field.help_text: 434 help_text = help_text_html % force_unicode(field.help_text) 435 else: 436 help_text = u'' 437 438 output.append(normal_row % { 439 'errors': force_unicode(bf_errors), 440 'label': force_unicode(label), 441 'field': unicode(bf), 442 'help_text': help_text, 443 'html_class_attr': html_class_attr 444 }) 445 446 if top_errors: 447 output.insert(0, error_row % force_unicode(top_errors)) 448 449 if hidden_fields: # Insert any hidden fields in the last row. 450 str_hidden = u''.join(hidden_fields) 451 if output: 452 last_row = output[-1] 453 # Chop off the trailing row_ender (e.g. '</td></tr>') and 454 # insert the hidden fields. 455 if not last_row.endswith(row_ender): 456 # This can happen in the as_p() case (and possibly others 457 # that users write): if there are only top errors, we may 458 # not be able to conscript the last row for our purposes, 459 # so insert a new, empty row. 460 last_row = (normal_row % {'errors': '', 'label': '', 461 'field': '', 'help_text':'', 462 'html_class_attr': html_class_attr}) 463 output.append(last_row) 464 output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender 465 else: 466 # If there aren't any rows in the output, just append the 467 # hidden fields. 468 output.append(str_hidden) 469 470 return mark_safe(u'\n'.join(output)) 471 472 def as_table(self): 473 "Returns this fieldset rendered as HTML <tr>s -- excluding the <table>, <fieldset> and <legend> tags." 474 return self._html_output( 475 normal_row = u'<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', 476 error_row = u'<tr><td colspan="2">%s</td></tr>', 477 row_ender = u'</td></tr>', 478 help_text_html = u'<br /><span class="helptext">%s</span>', 479 errors_on_separate_row = False) 480 481 def as_ul(self): 482 "Returns this fieldset rendered as HTML <li>s -- excluding the <ul>, <fieldset> and <legend> tags." 483 return self._html_output( 484 normal_row = u'<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>', 485 error_row = u'<li>%s</li>', 486 row_ender = '</li>', 487 help_text_html = u' <span class="helptext">%s</span>', 488 errors_on_separate_row = False) 489 490 def as_p(self): 491 "Returns this fieldset rendered as HTML <p>s -- excluding the <fieldset> and <legend> tags." 492 return self._html_output( 493 normal_row = u'<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>', 494 error_row = u'%s', 495 row_ender = '</p>', 496 help_text_html = u' <span class="helptext">%s</span>', 497 errors_on_separate_row = True) 498 499 def legend_tag(self, contents=None, attrs=None): 500 """ 501 Wraps the given contents in a <legend>. Does not HTML-escape the contents. 502 If contents aren't given, uses the fieldset's HTML-escaped legend. 503 504 If attrs are given, they're used as HTML attributes on the <legend> tag. 505 """ 506 if contents is None and not self.legend is None: 507 contents = conditional_escape(self.legend) 508 attrs = attrs and flatatt(attrs) or '' 509 if not contents is None: 510 return mark_safe(u'<legend%s>%s</legend>' % (attrs, force_unicode(self.legend))) 511 return None 512 513 def hidden_fields(self): 514 """ 515 Returns a list of all the BoundField objects that are hidden fields. 516 Useful for manual form layout in templates. 517 """ 518 return [field for field in self if field.is_hidden] 519 520 def visible_fields(self): 521 """ 522 Returns a list of BoundField objects that aren't hidden fields. 523 The opposite of the hidden_fields() method. 524 """ 525 return [field for field in self if not field.is_hidden] 526 392 527 class BoundField(StrAndUnicode): 393 528 "A Field plus data" 394 529 def __init__(self, form, field, name): -
django/forms/models.py
diff --git a/django/forms/models.py b/django/forms/models.py index b65f067..467d00e 100644
a b from __future__ import absolute_import 8 8 from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError 9 9 from django.core.validators import EMPTY_VALUES 10 10 from django.forms.fields import Field, ChoiceField 11 from django.forms.forms import BaseForm, get_declared_fields 11 from django.forms.forms import (BaseForm, BaseFormOptions, BaseFormMetaclass, 12 get_declared_fields) 12 13 from django.forms.formsets import BaseFormSet, formset_factory 13 14 from django.forms.util import ErrorList 14 15 from django.forms.widgets import (SelectMultiple, HiddenInput, 15 MultipleHiddenInput , media_property)16 MultipleHiddenInput) 16 17 from django.utils.encoding import smart_unicode, force_unicode 17 18 from django.utils.datastructures import SortedDict 18 19 from django.utils.text import get_text_list, capfirst … … def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_c 175 176 ) 176 177 return field_dict 177 178 178 class ModelFormOptions( object):179 class ModelFormOptions(BaseFormOptions): 179 180 def __init__(self, options=None): 181 super(ModelFormOptions, self).__init__(options) 180 182 self.model = getattr(options, 'model', None) 181 183 self.fields = getattr(options, 'fields', None) 182 184 self.exclude = getattr(options, 'exclude', None) 183 185 self.widgets = getattr(options, 'widgets', None) 184 186 185 186 class ModelFormMetaclass(type): 187 class ModelFormMetaclass(BaseFormMetaclass): 187 188 def __new__(cls, name, bases, attrs): 188 189 formfield_callback = attrs.pop('formfield_callback', None) 189 190 try: 190 parents = [b for b in bases if issubclass(b, ModelForm)]191 parents = [b for b in bases if issubclass(b, BaseModelForm)] 191 192 except NameError: 192 193 # We are defining ModelForm itself. 193 194 parents = None … … class ModelFormMetaclass(type): 196 197 attrs) 197 198 if not parents: 198 199 return new_class 199 200 if 'media' not in attrs: 201 new_class.media = media_property(new_class) 200 # Override BaseFormOptions with ModelFormOptions (which is actually 201 # BaseFormOptions' subclass). This obviously causes BaseFormOptions.__init__() 202 # being called twice through the form class definition, but it's a price we can 203 # pay for the less redundant code. 202 204 opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) 203 205 if opts.model: 204 206 # If a model is defined, extract form fields from it. … … class BaseModelForm(BaseForm): 305 307 306 308 def _post_clean(self): 307 309 opts = self._meta 308 # Update the model instance with self.cleaned_data. 310 # Update the model instance with self.cleaned_data.ModelForm 309 311 self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) 310 312 311 313 exclude = self._get_validation_exclusions() -
docs/topics/forms/index.txt
diff --git a/docs/topics/forms/index.txt b/docs/topics/forms/index.txt index 18e55f5..bd47732 100644
a b tag:: 388 388 If you find yourself doing this often, you might consider creating a custom 389 389 :ref:`inclusion tag<howto-custom-template-tags-inclusion-tags>`. 390 390 391 Fieldsets 392 --------- 393 394 .. versionadded:: development 395 396 Having a complex form you may want to organize its fields in logical groups. 397 In Django you can do that using fieldsets. Fieldsets allow you to iterate 398 through their fields and are autmatically rendered by form using ``<fieldset>`` 399 HTML tag and its subtag ``<legend>``. 400 401 402 Fieldsets are defined using ``Meta`` options class. If you're familiar 403 with :attr:`~django.contrib.admin.ModelAdmin.fieldsets` from Django admin 404 options, you alreadyknow the syntax: 405 406 .. code-block:: python 407 408 class PersonForm(forms.Form): 409 home_phone = CharField() 410 cell_phone = CharField() 411 first_name = CharField() 412 last_name = CharField() 413 414 class Meta: 415 fieldsets = ( 416 (None, { 417 'fields': ('first_name', 'last_name',), 418 }), 419 ("Phone numbers", { 420 'fields': ('cell_phone', 'home_phone',), 421 }), 422 ) 423 424 Having above example form you may render it in a template just like a normal form:: 425 426 <form action="" method="post"> 427 {{ form.as_table }} 428 <input type="submit" value="Send" /> 429 </form> 430 431 Except now instead of one ``<table>`` element, ``as_table`` method will 432 print two tables wrapped up in ``<fieldset>`` tags:: 433 434 <form action="" method="post"> 435 <fieldset> 436 <table> 437 <tr> 438 <th><label for="id_first_name">First name:</label></th> 439 <td><input type="text" name="first_name" id="id_first_name" /></td> 440 </tr> 441 <tr> 442 <th><label for="id_last_name">Last name:</label></th> 443 <td><input type="text" name="last_name" id="id_last_name" /></td> 444 </tr> 445 </table> 446 </fieldset> 447 <fieldset> 448 <legend>Phone numbers</legend> 449 <table> 450 <tr> 451 <th><label for="id_cell_phone">Cell phone:</label></th> 452 <td><input type="text" name="cell_phone" id="id_cell_phone" /></td> 453 </tr> 454 <tr> 455 <th><label for="id_home_phone">Home phone:</label></th> 456 <td><input type="text" name="home_phone" id="id_home_phone" /></td> 457 </tr> 458 </table> 459 </fieldset> 460 <input type="submit" value="Send" /> 461 </form> 462 463 You can also customize your output looping through form's fieldsets and using 464 their methods -- ``as_table``, ``as_ul`` and ``as_p`` -- which behave just like 465 their equivalents from ``Form`` class and using a ``legend_tag`` method:: 466 467 <form action="" method="post"> 468 {% for fieldset in form.fieldsets %} 469 <fieldset> 470 {{ fieldset.legend_tag }} 471 <table> 472 {{ fieldset.as_table }} 473 </table> 474 </fieldset> 475 {% endfor %} 476 <input type="submit" value="Send" /> 477 </form> 478 479 You can be even more specific and loop through all fields of all fieldsets:: 480 481 <form action="" method="post"> 482 {% for fieldset in form.fieldsets %} 483 <fieldset> 484 {{ fieldset.legend_tag }} 485 <ul> 486 {% for field in fieldset %} 487 <li> 488 {{ field.label_tag }} 489 {{ field }} 490 </li> 491 {% endfor %} 492 </ul> 493 </fieldset> 494 {% endfor %} 495 <input type="submit" value="Send" /> 496 </form> 497 498 You can also loop though fieldset ``hidden_fields`` and ``visible_fields`` just 499 line in a form class. 500 391 501 Further topics 392 502 ============== 393 503 -
tests/regressiontests/forms/tests/__init__.py
diff --git a/tests/regressiontests/forms/tests/__init__.py b/tests/regressiontests/forms/tests/__init__.py index 8e2150c..8c3d0fb 100644
a b from .util import FormsUtilTestCase 19 19 from .validators import TestFieldWithValidators 20 20 from .widgets import (FormsWidgetTestCase, FormsI18NWidgetsTestCase, 21 21 WidgetTests, ClearableFileInputTests) 22 from .fieldsets import FieldsetsTestCase -
new file tests/regressiontests/forms/tests/fieldsets.py
diff --git a/tests/regressiontests/forms/tests/fieldsets.py b/tests/regressiontests/forms/tests/fieldsets.py new file mode 100644 index 0000000..79ccabb
- + 1 # -*- coding: utf-8 -*- 2 import datetime 3 4 from django.core.files.uploadedfile import SimpleUploadedFile 5 from django.forms import * 6 from django import forms 7 from django.http import QueryDict 8 from django.template import Template, Context 9 from django.utils.datastructures import MultiValueDict, MergeDict 10 from django.utils.safestring import mark_safe 11 from django.utils.unittest import TestCase 12 13 14 class PersonWithoutFormfields(Form): 15 first_name = CharField() 16 last_name = CharField() 17 birthday = DateField() 18 band = CharField() 19 secret = CharField(widget=HiddenInput) 20 21 class Person(PersonWithoutFormfields): 22 class Meta: 23 fieldsets = ( 24 (None, { 25 'fields': ('first_name', 'last_name', 'birthday'), 26 }), 27 ("Additional fields", { 28 'fields': ('band', 'secret'), 29 }), 30 ) 31 32 class FieldsetsTestCase(TestCase): 33 34 some_data = { 35 'first_name': u'John', 36 'last_name': u'Lennon', 37 'birthday': u'1940-10-9', 38 'band': u'The Beatles', 39 'secret': u'he didnt say', 40 } 41 42 def test_simple_rendering(self): 43 # Pass a dictionary to a Form's __init__(). 44 p = Person(self.some_data) 45 # as_table 46 self.assertEqual(str(p), """<fieldset> 47 <table> 48 <tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" value="John" id="id_first_name" /></td></tr> 49 <tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" value="Lennon" id="id_last_name" /></td></tr> 50 <tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></td></tr> 51 </table> 52 </fieldset> 53 <fieldset> 54 <legend>Additional fields</legend> 55 <table> 56 <tr><th><label for="id_band">Band:</label></th><td><input type="text" name="band" value="The Beatles" id="id_band" /><input type="hidden" name="secret" value="he didnt say" id="id_secret" /></td></tr> 57 </table> 58 </fieldset>""") 59 self.assertEqual(str(p), unicode(p)) 60 self.assertEqual(str(p), p.as_table()) 61 # as_ul 62 self.assertEqual(p.as_ul(), """<fieldset> 63 <ul> 64 <li><label for="id_first_name">First name:</label> <input type="text" name="first_name" value="John" id="id_first_name" /></li> 65 <li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" value="Lennon" id="id_last_name" /></li> 66 <li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></li> 67 </ul> 68 </fieldset> 69 <fieldset> 70 <legend>Additional fields</legend> 71 <ul> 72 <li><label for="id_band">Band:</label> <input type="text" name="band" value="The Beatles" id="id_band" /><input type="hidden" name="secret" value="he didnt say" id="id_secret" /></li> 73 </ul> 74 </fieldset>""") 75 # as_p 76 self.assertEqual(p.as_p(), """<fieldset> 77 78 <p><label for="id_first_name">First name:</label> <input type="text" name="first_name" value="John" id="id_first_name" /></p> 79 <p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" value="Lennon" id="id_last_name" /></p> 80 <p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></p> 81 82 </fieldset> 83 <fieldset> 84 <legend>Additional fields</legend> 85 86 <p><label for="id_band">Band:</label> <input type="text" name="band" value="The Beatles" id="id_band" /><input type="hidden" name="secret" value="he didnt say" id="id_secret" /></p> 87 88 </fieldset>""") # Additional blank lines are ok 89 90 def test_single_fieldset_rendering(self): 91 # Pass a dictionary to a Form's __init__(). 92 p = Person(self.some_data) 93 # as_table 94 self.assertEqual(str(p.fieldsets[0]), """<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" value="John" id="id_first_name" /></td></tr> 95 <tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" value="Lennon" id="id_last_name" /></td></tr> 96 <tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></td></tr>""") 97 self.assertEqual(str(p.fieldsets[0]), unicode(p.fieldsets[0])) 98 self.assertEqual(str(p.fieldsets[0]), p.fieldsets[0].as_table()) 99 self.assertEqual(str(p.fieldsets[1]), """<tr><th><label for="id_band">Band:</label></th><td><input type="text" name="band" value="The Beatles" id="id_band" /><input type="hidden" name="secret" value="he didnt say" id="id_secret" /></td></tr>""") 100 self.assertEqual(str(p.fieldsets[1]), p.fieldsets[1].as_table()) 101 # as_ul 102 self.assertEqual(p.fieldsets[0].as_ul(), """<li><label for="id_first_name">First name:</label> <input type="text" name="first_name" value="John" id="id_first_name" /></li> 103 <li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" value="Lennon" id="id_last_name" /></li> 104 <li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></li>""") 105 self.assertEqual(p.fieldsets[1].as_ul(), """<li><label for="id_band">Band:</label> <input type="text" name="band" value="The Beatles" id="id_band" /><input type="hidden" name="secret" value="he didnt say" id="id_secret" /></li>""") 106 # as_p 107 self.assertEqual(p.fieldsets[0].as_p(), """<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" value="John" id="id_first_name" /></p> 108 <p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" value="Lennon" id="id_last_name" /></p> 109 <p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></p>""") 110 self.assertEqual(p.fieldsets[1].as_p(), """<p><label for="id_band">Band:</label> <input type="text" name="band" value="The Beatles" id="id_band" /><input type="hidden" name="secret" value="he didnt say" id="id_secret" /></p>""") 111 112 def test_fieldset_fields_iteration(self): 113 # Pass a dictionary to a Form's __init__(). 114 p = Person(self.some_data) 115 for fieldset in p.fieldsets: 116 for field in fieldset: 117 pass 118 self.assertEqual(set([field.name for field in p.fieldsets[0].visible_fields()]), set(['first_name', 'last_name', 'birthday'])) 119 self.assertEqual(len(p.fieldsets[0].hidden_fields()), 0) 120 self.assertEqual(set([field.name for field in p.fieldsets[1].visible_fields()]), set(['band'])) 121 self.assertEqual(set([field.name for field in p.fieldsets[1].hidden_fields()]), set(['secret'])) 122 123 def test_legend_tag(self): 124 # Pass a dictionary to a Form's __init__(). 125 p = Person(self.some_data) 126 self.assertIsNone(p.fieldsets[0].legend_tag()) 127 self.assertEqual(p.fieldsets[1].legend_tag(), """<legend>Additional fields</legend>""") 128 -
tests/regressiontests/forms/tests/forms.py
diff --git a/tests/regressiontests/forms/tests/forms.py b/tests/regressiontests/forms/tests/forms.py index a37cc2e..8a9dc40 100644
a b class FormsTestCase(TestCase): 1753 1753 1754 1754 form = EventForm() 1755 1755 self.assertEqual(form.as_ul(), u'<input type="hidden" name="happened_at_0" id="id_happened_at_0" /><input type="hidden" name="happened_at_1" id="id_happened_at_1" />') 1756 1757 def test_meta_options(self): 1758 class MetaOptionsForm(Form): 1759 class Meta: 1760 fieldsets = 0xDEADBEEF 1761 some_nonexising_option = True 1762 class MetaOptionsDerivantForm(MetaOptionsForm): 1763 pass 1764 # Test classes 1765 self.assertEqual(MetaOptionsForm._meta.fieldsets, 0xDEADBEEF) 1766 self.assertEqual(MetaOptionsDerivantForm._meta.fieldsets, 0xDEADBEEF) 1767 self.assertFalse(hasattr(MetaOptionsForm._meta, 'some_nonexising_option')) 1768 self.assertFalse(hasattr(MetaOptionsDerivantForm._meta, 'some_nonexising_option')) 1769 # Test instances 1770 meta_options_form = MetaOptionsForm() 1771 meta_options_derivant_form = MetaOptionsDerivantForm() 1772 self.assertEqual(meta_options_form._meta.fieldsets, 0xDEADBEEF) 1773 self.assertEqual(meta_options_derivant_form._meta.fieldsets, 0xDEADBEEF) 1774 self.assertFalse(hasattr(meta_options_form._meta, 'some_nonexising_option')) 1775 self.assertFalse(hasattr(meta_options_derivant_form._meta, 'some_nonexising_option')) 1776 1777 def test_meta_options_override(self): 1778 class MetaOptionsForm(Form): 1779 class Meta: 1780 fieldsets = 0xDEADBEEF 1781 class MetaOptionsDerivantForm(MetaOptionsForm): 1782 class Meta: 1783 fieldsets = 0xCAFEBABE 1784 # Test classes 1785 self.assertEqual(MetaOptionsForm._meta.fieldsets, 0xDEADBEEF) 1786 self.assertEqual(MetaOptionsDerivantForm._meta.fieldsets, 0xCAFEBABE) 1787 # Test instances 1788 meta_options_form = MetaOptionsForm() 1789 meta_options_derivant_form = MetaOptionsDerivantForm() 1790 self.assertEqual(meta_options_form._meta.fieldsets, 0xDEADBEEF) 1791 self.assertEqual(meta_options_derivant_form._meta.fieldsets, 0xCAFEBABE) 1792 1793 def test_meta_option_defaults(self): 1794 class MetaOptionsForm(Form): 1795 pass 1796 # Test classes 1797 self.assertIsNone(MetaOptionsForm._meta.fieldsets) 1798 # Test instance 1799 meta_options_form = MetaOptionsForm() 1800 self.assertIsNone(meta_options_form._meta.fieldsets) 1801