Ticket #7568: 0001-started-to-move-newforms-closer-to-pep8-standards.diff
| File 0001-started-to-move-newforms-closer-to-pep8-standards.diff, 30.2 kB (added by stevemilner, 2 years ago) |
|---|
-
a/django/newforms/extras/widgets.py
old new 9 9 from django.utils.dates import MONTHS 10 10 from django.utils.safestring import mark_safe 11 11 12 __all__ = ('SelectDateWidget', )12 __all__ = ('SelectDateWidget', ) 13 13 14 14 RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$') 15 15 16 16 17 class SelectDateWidget(Widget): 17 18 """ 18 19 A Widget that splits date input into three <select> boxes. … … 25 26 year_field = '%s_year' 26 27 27 28 def __init__(self, attrs=None, years=None): 28 # years is an optional list/tuple of years to use in the "year" select box. 29 # years is an optional list/tuple of years to use in the 30 # "year" select box. 29 31 self.attrs = attrs or {} 30 32 if years: 31 33 self.years = years … … 41 43 if isinstance(value, basestring): 42 44 match = RE_DATE.match(value) 43 45 if match: 44 year_val, month_val, day_val = [int(v) for v in match.groups()] 46 year_val, month_val, day_val = [ 47 int(v) for v in match.groups()] 45 48 46 49 output = [] 47 50 … … 53 56 month_choices = MONTHS.items() 54 57 month_choices.sort() 55 58 local_attrs = self.build_attrs(id=self.month_field % id_) 56 select_html = Select(choices=month_choices).render(self.month_field % name, month_val, local_attrs) 59 select_html = Select(choices=month_choices).render( 60 self.month_field % name, month_val, local_attrs) 57 61 output.append(select_html) 58 62 59 63 day_choices = [(i, i) for i in range(1, 32)] 60 64 local_attrs['id'] = self.day_field % id_ 61 select_html = Select(choices=day_choices).render(self.day_field % name, day_val, local_attrs) 65 select_html = Select(choices=day_choices).render( 66 self.day_field % name, day_val, local_attrs) 62 67 output.append(select_html) 63 68 64 69 year_choices = [(i, i) for i in self.years] 65 70 local_attrs['id'] = self.year_field % id_ 66 select_html = Select(choices=year_choices).render(self.year_field % name, year_val, local_attrs) 71 select_html = Select(choices=year_choices).render( 72 self.year_field % name, year_val, local_attrs) 67 73 output.append(select_html) 68 74 69 75 return mark_safe(u'\n'.join(output)) … … 73 79 id_for_label = classmethod(id_for_label) 74 80 75 81 def value_from_datadict(self, data, files, name): 76 y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name) 82 y = data.get(self.year_field % name) 83 m = data.get(self.month_field % name) 84 d = data.get(self.day_field % name) 77 85 if y and m and d: 78 86 return '%s-%s-%s' % (y, m, d) 79 87 return data.get(name, None) -
a/django/newforms/fields.py
old new 21 21 from django.utils.encoding import StrAndUnicode, smart_unicode, smart_str 22 22 23 23 from util import ErrorList, ValidationError 24 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput 24 from widgets import TextInput, PasswordInput, HiddenInput 25 from widgets import MultipleHiddenInput, FileInput, CheckboxInput, Select 26 from widgets import NullBooleanSelect, SelectMultiple, DateTimeInput 25 27 26 28 27 29 __all__ = ( … … 40 42 41 43 42 44 class Field(object): 43 widget = TextInput # Default widget to use when rendering this type of Field. 44 hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden". 45 # Default widget to use when rendering this type of Field. 46 widget = TextInput 47 # Default widget to use when rendering this as "hidden". 48 hidden_widget = HiddenInput 45 49 default_error_messages = { 46 50 'required': _(u'This field is required.'), 47 51 'invalid': _(u'Enter a valid value.'), … … 119 123 result.widget = copy.deepcopy(self.widget, memo) 120 124 return result 121 125 126 122 127 class CharField(Field): 123 128 default_error_messages = { 124 129 'max_length': _(u'Ensure this value has at most %(max)d characters (it has %(length)d).'), -
a/django/newforms/forms.py
old new 17 17 18 18 NON_FIELD_ERRORS = '__all__' 19 19 20 20 21 def pretty_name(name): 21 22 "Converts 'first_name' to 'First name'" 22 23 name = name[0].upper() + name[1:] 23 24 return name.replace('_', ' ') 24 25 26 25 27 def get_declared_fields(bases, attrs, with_base_fields=True): 26 28 """ 27 29 Create a list of form field instances from the passed in 'attrs', plus any … … 32 34 Otherwise, only fields in the 'declared_fields' attribute on the bases are 33 35 used. The distinction is useful in ModelForm subclassing. 34 36 """ 35 fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] 37 fields = [(field_name, attrs.pop(field_name)) \ 38 for field_name, obj in attrs.items() if isinstance(obj, Field)] 36 39 fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) 37 40 38 41 # If this class is subclassing another Form, add that Form's fields. … … 49 52 50 53 return SortedDict(fields) 51 54 55 52 56 class DeclarativeFieldsMetaclass(type): 53 57 """ 54 58 Metaclass that converts Field attributes to a dictionary called 55 59 'base_fields', taking into account parent class 'base_fields' as well. 56 60 """ 61 57 62 def __new__(cls, name, bases, attrs): 58 63 attrs['base_fields'] = get_declared_fields(bases, attrs) 59 64 return type.__new__(cls, name, bases, attrs) 60 65 66 61 67 class BaseForm(StrAndUnicode): 62 # This is the main implementation of all the Form logic. Note that this 63 # class is different than Form. See the comments by the Form class for more 64 # information. Any improvements to the form API should be made to *this* 65 # class, not to the Form class. 68 """ 69 This is the main implementation of all the Form logic. Note that this 70 class is different than Form. See the comments by the Form class for more 71 information. Any improvements to the form API should be made to *this* 72 class, not to the Form class. 73 """ 74 66 75 def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, 67 76 initial=None, error_class=ErrorList, label_suffix=':'): 68 77 self.is_bound = data is not None or files is not None … … 118 127 119 128 Subclasses may wish to override. 120 129 """ 121 return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name 130 return self.prefix and ('%s-%s' % (self.prefix, field_name)) or \ 131 field_name 122 132 123 def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row): 124 "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." 125 top_errors = self.non_field_errors() # Errors that should be displayed above all fields. 133 def _html_output(self, normal_row, error_row, row_ender, 134 help_text_html, errors_on_separate_row): 135 """ 136 Helper function for outputting HTML. Used by as_table(), 137 as_ul(), as_p(). 138 """ 139 # Errors that should be displayed above all fields. 140 top_errors = self.non_field_errors() 126 141 output, hidden_fields = [], [] 127 142 for name, field in self.fields.items(): 128 143 bf = BoundField(self, field, name) 129 bf_errors = self.error_class([escape(error) for error in bf.errors]) # Escape and cache in local variable. 144 # Escape and cache in local variable. 145 bf_errors = self.error_class([escape(error) \ 146 for error in bf.errors]) 130 147 if bf.is_hidden: 131 148 if bf_errors: 132 top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors]) 149 top_errors.extend([u'(Hidden field %s) %s' % ( 150 name, force_unicode(e)) for e in bf_errors]) 133 151 hidden_fields.append(unicode(bf)) 134 152 else: 135 153 if errors_on_separate_row and bf_errors: … … 148 166 help_text = help_text_html % force_unicode(field.help_text) 149 167 else: 150 168 help_text = u'' 151 output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text}) 169 output.append(normal_row % { 170 'errors': force_unicode(bf_errors), 171 'label': force_unicode(label), 172 'field': unicode(bf), 173 'help_text': help_text}) 152 174 if top_errors: 153 175 output.insert(0, error_row % force_unicode(top_errors)) 154 176 if hidden_fields: # Insert any hidden fields in the last row. … … 157 179 last_row = output[-1] 158 180 # Chop off the trailing row_ender (e.g. '</td></tr>') and 159 181 # insert the hidden fields. 160 output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender 182 output[-1] = last_row[:-len(row_ender)] + str_hidden + \ 183 row_ender 161 184 else: 162 185 # If there aren't any rows in the output, just append the 163 186 # hidden fields. … … 165 188 return mark_safe(u'\n'.join(output)) 166 189 167 190 def as_table(self): 168 "Returns this form rendered as HTML <tr>s -- excluding the <table></table>." 169 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) 191 """ 192 Returns this form rendered as HTML <tr>s -- excluding the 193 <table></table>. 194 """ 195 return self._html_output( 196 u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s\ 197 </td></tr>', 198 u'<tr><td colspan="2">%s</td></tr>', 199 '</td></tr>', 200 u'<br />%s', 201 False) 170 202 171 203 def as_ul(self): 172 204 "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." 173 return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False) 205 return self._html_output( 206 u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', 207 u'<li>%s</li>', 208 '</li>', 209 u' %s', 210 False) 174 211 175 212 def as_p(self): 176 213 "Returns this form rendered as HTML <p>s." 177 return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True) 214 return self._html_output( 215 u'<p>%(label)s %(field)s%(help_text)s</p>', 216 u'%s', 217 '</p>', 218 u' %s', 219 True) 178 220 179 221 def non_field_errors(self): 180 222 """ … … 197 239 # value_from_datadict() gets the data from the data dictionaries. 198 240 # Each widget type knows how to retrieve its own data, because some 199 241 # widgets split data over several HTML fields. 200 value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) 242 value = field.widget.value_from_datadict(self.data, 243 self.files, self.add_prefix(name)) 201 244 try: 202 245 if isinstance(field, FileField): 203 246 initial = self.initial.get(name, field.initial) … … 238 281 return True 239 282 return False 240 283 284 241 285 class Form(BaseForm): 242 286 "A collection of Fields, plus their associated data." 243 287 # This is a separate class from BaseForm in order to abstract the way … … 247 291 # BaseForm itself has no way of designating self.fields. 248 292 __metaclass__ = DeclarativeFieldsMetaclass 249 293 294 250 295 class BoundField(StrAndUnicode): 251 296 "A Field plus data" 297 252 298 def __init__(self, form, field, name): 253 299 self.form = form 254 300 self.field = field … … 294 340 295 341 def as_text(self, attrs=None): 296 342 """ 297 Returns a string of HTML for representing this as an <input type="text">. 343 Returns a string of HTML for representing this as 344 an <input type="text">. 298 345 """ 299 346 return self.as_widget(TextInput(), attrs) 300 347 … … 304 351 305 352 def as_hidden(self, attrs=None): 306 353 """ 307 Returns a string of HTML for representing this as an <input type="hidden">. 354 Returns a string of HTML for representing this as an 355 <input type="hidden">. 308 356 """ 309 357 return self.as_widget(self.field.hidden_widget(), attrs) 310 358 … … 312 360 """ 313 361 Returns the data for this BoundField, or None if it wasn't given. 314 362 """ 315 return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name) 363 return self.field.widget.value_from_datadict( 364 self.form.data, self.form.files, self.html_name) 316 365 data = property(_data) 317 366 318 367 def label_tag(self, contents=None, attrs=None): 319 368 """ 320 Wraps the given contents in a <label>, if the field has an ID attribute.321 Does not HTML-escape the contents. If contents aren't given, uses the322 field's HTML-escaped label.369 Wraps the given contents in a <label>, if the field has an ID 370 attribute. Does not HTML-escape the contents. If contents aren't 371 given, uses the field's HTML-escaped label. 323 372 324 If attrs are given, they're used as HTML attributes on the <label> tag. 373 If attrs are given, they're used as HTML attributes on the 374 <label> tag. 325 375 """ 326 376 contents = contents or escape(self.label) 327 377 widget = self.field.widget 328 378 id_ = widget.attrs.get('id') or self.auto_id 329 379 if id_: 330 380 attrs = attrs and flatatt(attrs) or '' 331 contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, contents) 381 contents = '<label for="%s"%s>%s</label>' % ( 382 widget.id_for_label(id_), attrs, contents) 332 383 return mark_safe(contents) 333 384 334 385 def _is_hidden(self): … … 339 390 def _auto_id(self): 340 391 """ 341 392 Calculates and returns the ID attribute for this BoundField, if the 342 associated Form has specified auto_id. Returns an empty string otherwise. 393 associated Form has specified auto_id. Returns an empty string 394 otherwise. 343 395 """ 344 396 auto_id = self.form.auto_id 345 397 if auto_id and '%s' in smart_unicode(auto_id): -
a/django/newforms/widgets.py
old new 25 25 'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget', 26 26 ) 27 27 28 28 29 class Widget(object): 29 is_hidden = False # Determines whether this corresponds to an <input type="hidden">. 30 needs_multipart_form = False # Determines does this widget need multipart-encrypted form 30 is_hidden = False # Determines whether this corresponds to 31 #an <input type="hidden">. 32 needs_multipart_form = False # Determines does this widget 33 # need multipart-encrypted form 31 34 32 35 def __init__(self, attrs=None): 33 36 if attrs is not None: … … 77 80 return id_ 78 81 id_for_label = classmethod(id_for_label) 79 82 83 80 84 class Input(Widget): 81 85 """ 82 86 Base class for all <input> widgets (except type='checkbox' and … … 85 89 input_type = None # Subclasses must define this. 86 90 87 91 def render(self, name, value, attrs=None): 88 if value is None: value = '' 92 if value is None: 93 value = '' 89 94 final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) 90 95 if value != '': 91 96 # Only add the 'value' attribute if a value is non-empty. 92 97 final_attrs['value'] = force_unicode(value) 93 98 return mark_safe(u'<input%s />' % flatatt(final_attrs)) 94 99 100 95 101 class TextInput(Input): 96 102 input_type = 'text' 97 103 104 98 105 class PasswordInput(Input): 99 106 input_type = 'password' 100 107 … … 103 110 self.render_value = render_value 104 111 105 112 def render(self, name, value, attrs=None): 106 if not self.render_value: value=None 113 if not self.render_value: 114 value=None 107 115 return super(PasswordInput, self).render(name, value, attrs) 108 116 117 109 118 class HiddenInput(Input): 110 119 input_type = 'hidden' 111 120 is_hidden = True 112 121 122 113 123 class MultipleHiddenInput(HiddenInput): 114 124 """ 115 125 A widget that handles <input type="hidden"> for fields that have a list 116 126 of values. 117 127 """ 128 118 129 def __init__(self, attrs=None, choices=()): 119 130 super(MultipleHiddenInput, self).__init__(attrs) 120 131 # choices can be any iterable 121 132 self.choices = choices 122 133 123 134 def render(self, name, value, attrs=None, choices=()): 124 if value is None: value = [] 135 if value is None: 136 value = [] 125 137 final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) 126 138 return mark_safe(u'\n'.join([(u'<input%s />' % 127 139 flatatt(dict(value=force_unicode(v), **final_attrs))) … … 132 144 return data.getlist(name) 133 145 return data.get(name, None) 134 146 147 135 148 class FileInput(Input): 136 149 input_type = 'file' 137 150 needs_multipart_form = True … … 143 156 "File widgets take data from FILES, not POST" 144 157 return files.get(name, None) 145 158 159 146 160 class Textarea(Widget): 161 147 162 def __init__(self, attrs=None): 148 163 # The 'rows' and 'cols' attributes are required for HTML correctness. 149 164 self.attrs = {'cols': '40', 'rows': '10'} … … 151 166 self.attrs.update(attrs) 152 167 153 168 def render(self, name, value, attrs=None): 154 if value is None: value = '' 169 if value is None: 170 value = '' 155 171 value = force_unicode(value) 156 172 final_attrs = self.build_attrs(attrs, name=name) 157 173 return mark_safe(u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), 158 174 conditional_escape(force_unicode(value)))) 159 175 176 160 177 class DateTimeInput(Input): 161 178 input_type = 'text' 162 179 format = '%Y-%m-%d %H:%M:%S' # '2006-10-25 14:30:59' … … 173 190 value = value.strftime(self.format) 174 191 return super(DateTimeInput, self).render(name, value, attrs) 175 192 193 176 194 class CheckboxInput(Widget): 195 177 196 def __init__(self, attrs=None, check_test=bool): 178 197 super(CheckboxInput, self).__init__(attrs) 179 198 # check_test is a callable that takes a value and returns True … … 198 217 # A missing value means False because HTML form submission does not 199 218 # send results for unselected checkboxes. 200 219 return False 201 return super(CheckboxInput, self).value_from_datadict(data, files, name) 220 return super(CheckboxInput, self).value_from_datadict( 221 data, files, name) 222 202 223 203 224 class Select(Widget): 225 204 226 def __init__(self, attrs=None, choices=()): 205 227 super(Select, self).__init__(attrs) 206 228 # choices can be any iterable, but we may need to render this widget … … 209 231 self.choices = list(choices) 210 232 211 233 def render(self, name, value, attrs=None, choices=()): 212 if value is None: value = '' 234 if value is None: 235 value = '' 213 236 final_attrs = self.build_attrs(attrs, name=name) 214 237 output = [u'<select%s>' % flatatt(final_attrs)] 215 238 # Normalize to string. 216 239 str_value = force_unicode(value) 217 240 for option_value, option_label in chain(self.choices, choices): 218 241 option_value = force_unicode(option_value) 219 selected_html = (option_value == str_value) and u' selected="selected"' or '' 242 selected_html = (option_value == str_value) and \ 243 u' selected="selected"' or '' 220 244 output.append(u'<option value="%s"%s>%s</option>' % ( 221 245 escape(option_value), selected_html, 222 246 conditional_escape(force_unicode(option_label)))) 223 247 output.append(u'</select>') 224 248 return mark_safe(u'\n'.join(output)) 225 249 250 226 251 class NullBooleanSelect(Select): 227 252 """ 228 253 A Select Widget intended to be used with NullBooleanField. 229 254 """ 255 230 256 def __init__(self, attrs=None): 231 choices = ((u'1', ugettext('Unknown')), (u'2', ugettext('Yes')), (u'3', ugettext('No'))) 257 choices = ((u'1', ugettext('Unknown')), (u'2', ugettext('Yes')), 258 (u'3', ugettext('No'))) 232 259 super(NullBooleanSelect, self).__init__(attrs, choices) 233 260 234 261 def render(self, name, value, attrs=None, choices=()): … … 236 263 value = {True: u'2', False: u'3', u'2': u'2', u'3': u'3'}[value] 237 264 except KeyError: 238 265 value = u'1' 239 return super(NullBooleanSelect, self).render(name, value, attrs, choices) 266 return super(NullBooleanSelect, self).render( 267 name, value, attrs, choices) 240 268 241 269 def value_from_datadict(self, data, files, name): 242 270 value = data.get(name, None) 243 return {u'2': True, u'3': False, True: True, False: False}.get(value, None) 271 return {u'2': True, u'3': False, True: True, False: False}.get( 272 value, None) 273 244 274 245 275 class SelectMultiple(Widget): 276 246 277 def __init__(self, attrs=None, choices=()): 247 278 super(SelectMultiple, self).__init__(attrs) 248 279 # choices can be any iterable 249 280 self.choices = choices 250 281 251 282 def render(self, name, value, attrs=None, choices=()): 252 if value is None: value = [] 283 if value is None: 284 value = [] 253 285 final_attrs = self.build_attrs(attrs, name=name) 254 286 output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)] 255 str_values = set([force_unicode(v) for v in value]) # Normalize to strings. 287 # Normalize to strings. 288 str_values = set([force_unicode(v) for v in value]) 256 289 for option_value, option_label in chain(self.choices, choices): 257 290 option_value = force_unicode(option_value) 258 selected_html = (option_value in str_values) and ' selected="selected"' or '' 291 selected_html = (option_value in str_values) and \ 292 ' selected="selected"' or '' 259 293 output.append(u'<option value="%s"%s>%s</option>' % ( 260 294 escape(option_value), selected_html, 261 295 conditional_escape(force_unicode(option_label)))) … … 267 301 return data.getlist(name) 268 302 return data.get(name, None) 269 303 304 270 305 class RadioInput(StrAndUnicode): 271 306 """ 272 307 An object used by RadioFieldRenderer that represents a single … … 286 321 else: 287 322 label_for = '' 288 323 choice_label = conditional_escape(force_unicode(self.choice_label)) 289 return mark_safe(u'<label%s>%s %s</label>' % (label_for, self.tag(), choice_label)) 324 return mark_safe(u'<label%s>%s %s</label>' % ( 325 label_for, self.tag(), choice_label)) 290 326 291 327 def is_checked(self): 292 328 return self.value == self.choice_value … … 294 330 def tag(self): 295 331 if 'id' in self.attrs: 296 332 self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index) 297 final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value) 333 final_attrs = dict(self.attrs, type='radio', 334 name=self.name, value=self.choice_value) 298 335 if self.is_checked(): 299 336 final_attrs['checked'] = 'checked' 300 337 return mark_safe(u'<input%s />' % flatatt(final_attrs)) 301 338 339 302 340 class RadioFieldRenderer(StrAndUnicode): 303 341 """ 304 342 An object used by RadioSelect to enable customization of radio widgets. … … 310 348 311 349 def __iter__(self): 312 350 for i, choice in enumerate(self.choices): 313 yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i) 351 yield RadioInput(self.name, self.value, self.attrs.copy(), 352 choice, i) 314 353 315 354 def __getitem__(self, idx): 316 355 choice = self.choices[idx] # Let the IndexError propogate 317 return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx) 356 return RadioInput(self.name, self.value, self.attrs.copy(), 357 choice, idx) 318 358 319 359 def __unicode__(self): 320 360 return self.render() … … 324 364 return mark_safe(u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' 325 365 % force_unicode(w) for w in self])) 326 366 367 327 368 class RadioSelect(Select): 328 369 renderer = RadioFieldRenderer 329 370 … … 336 377 337 378 def get_renderer(self, name, value, attrs=None, choices=()): 338 379 """Returns an instance of the renderer.""" 339 if value is None: value = '' 380 if value is None: 381 value = '' 340 382 str_value = force_unicode(value) # Normalize to string. 341 383 final_attrs = self.build_attrs(attrs) 342 384 choices = list(chain(self.choices, choices)) … … 355 397 return id_ 356 398 id_for_label = classmethod(id_for_label) 357 399 400 358 401 class CheckboxSelectMultiple(SelectMultiple): 402 359 403 def render(self, name, value, attrs=None, choices=()): 360 if value is None: value = [] 404 if value is None: 405 value = [] 361 406 has_id = attrs and 'id' in attrs 362 407 final_attrs = self.build_attrs(attrs, name=name) 363 408 output = [u'<ul>'] 364 409 # Normalize to strings 365 410 str_values = set([force_unicode(v) for v in value]) 366 for i, (option_value, option_label) in enumerate(chain(self.choices, choices)): 411 for i, (option_value, option_label) in enumerate(chain( 412 self.choices, choices)): 367 413 # If an ID attribute was given, add a numeric index as a suffix, 368 414 # so that the checkboxes don't all have the same ID attribute. 369 415 if has_id: … … 371 417 label_for = u' for="%s"' % final_attrs['id'] 372 418 else: 373 419 label_for = '' 374 375 cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values) 420 421 cb = CheckboxInput(final_attrs, 422 check_test=lambda value: value in str_values) 376 423 option_value = force_unicode(option_value) 377 424 rendered_cb = cb.render(name, option_value) 378 425 option_label = conditional_escape(force_unicode(option_label)) 379 output.append(u'<li><label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label)) 426 output.append(u'<li><label%s>%s %s</label></li>' % ( 427 label_for, rendered_cb, option_label)) 380 428 output.append(u'</ul>') 381 429 return mark_safe(u'\n'.join(output)) 382 430 … … 387 435 return id_ 388 436 id_for_label = classmethod(id_for_label) 389 437 438 390 439 class MultiWidget(Widget): 391 440 """ 392 441 A widget that is composed of multiple widgets. … … 414 463 415 464 You'll probably want to use this class with MultiValueField. 416 465 """ 466 417 467 def __init__(self, widgets, attrs=None): 418 468 self.widgets = [isinstance(w, type) and w() or w for w in widgets] 419 469 super(MultiWidget, self).__init__(attrs) … … 433 483 widget_value = None 434 484 if id_: 435 485 final_attrs = dict(final_attrs, id='%s_%s' % (id_, i)) 436 output.append(widget.render(name + '_%s' % i, widget_value, final_attrs)) 486 output.append(widget.render(name + '_%s' % i, widget_value, 487 final_attrs)) 437 488 return mark_safe(self.format_output(output)) 438 489 439 490 def id_for_label(self, id_): … … 444 495 id_for_label = classmethod(id_for_label) 445 496 446 497 def value_from_datadict(self, data, files, name): 447 return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)] 498 return [widget.value_from_datadict(data, files, name + '_%s' % i) \ 499 for i, widget in enumerate(self.widgets)] 448 500 449 501 def format_output(self, rendered_widgets): 450 502 """ … … 464 516 """ 465 517 raise NotImplementedError('Subclasses must implement this method.') 466 518 519 467 520 class SplitDateTimeWidget(MultiWidget): 468 521 """ 469 522 A Widget that splits datetime input into two <input type="text"> boxes. 470 523 """ 524 471 525 def __init__(self, attrs=None): 472 526 widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs)) 473 527 super(SplitDateTimeWidget, self).__init__(widgets, attrs) … … 476 530 if value: 477 531 return [value.date(), value.time().replace(microsecond=0)] 478 532 return [None, None] 479
