=== modified file 'django/newforms/extras/widgets.py'
|
|
|
16 | 16 | This also serves as an example of a Widget that has more than one HTML |
17 | 17 | element and hence implements value_from_datadict. |
18 | 18 | """ |
| 19 | default_outer_class = 'select-date' |
| 20 | |
19 | 21 | month_field = '%s_month' |
20 | 22 | day_field = '%s_day' |
21 | 23 | year_field = '%s_year' |
22 | 24 | |
23 | | def __init__(self, attrs=None, years=None): |
| 25 | def __init__(self, attrs=None, outer_attrs=None, years=None): |
24 | 26 | # years is an optional list/tuple of years to use in the "year" select box. |
25 | | self.attrs = attrs or {} |
| 27 | super(SelectDateWidget, self).__init__(attrs, outer_attrs) |
26 | 28 | if years: |
27 | 29 | self.years = years |
28 | 30 | else: |
=== modified file 'django/newforms/forms.py'
|
|
|
131 | 131 | top_errors.extend(['(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors]) |
132 | 132 | hidden_fields.append(unicode(bf)) |
133 | 133 | else: |
| 134 | outer_attrs = bf.field.widget.outer_attrs |
| 135 | outer_class = outer_attrs.pop('class', '') |
| 136 | if bf.field.required: |
| 137 | outer_class += ' required' |
| 138 | if bf_errors: |
| 139 | outer_class += ' error' |
| 140 | if outer_class: |
| 141 | outer_attrs['class'] = outer_class.strip() |
134 | 142 | if errors_on_separate_row and bf_errors: |
135 | 143 | output.append(error_row % force_unicode(bf_errors)) |
136 | 144 | if bf.label: |
… |
… |
|
146 | 154 | help_text = help_text_html % force_unicode(field.help_text) |
147 | 155 | else: |
148 | 156 | help_text = u'' |
149 | | output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text}) |
| 157 | output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), |
| 158 | 'field': unicode(bf), 'help_text': help_text, 'outer_attrs': flatatt(outer_attrs)}) |
150 | 159 | if top_errors: |
151 | 160 | output.insert(0, error_row % top_errors) |
152 | 161 | if hidden_fields: # Insert any hidden fields in the last row. |
… |
… |
|
161 | 170 | |
162 | 171 | def as_table(self): |
163 | 172 | "Returns this form rendered as HTML <tr>s -- excluding the <table></table>." |
164 | | 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) |
| 173 | return self._html_output(u'<tr%(outer_attrs)s><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) |
165 | 174 | |
166 | 175 | def as_ul(self): |
167 | 176 | "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." |
168 | | return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False) |
| 177 | return self._html_output(u'<li%(outer_attrs)s>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False) |
169 | 178 | |
170 | 179 | def as_p(self): |
171 | 180 | "Returns this form rendered as HTML <p>s." |
172 | | return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True) |
| 181 | return self._html_output(u'<p%(outer_attrs)s>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True) |
173 | 182 | |
174 | 183 | def non_field_errors(self): |
175 | 184 | """ |
=== modified file 'django/newforms/widgets.py'
|
|
|
129 | 129 | __metaclass__ = MediaDefiningClass |
130 | 130 | is_hidden = False # Determines whether this corresponds to an <input type="hidden">. |
131 | 131 | needs_multipart_form = False # Determines does this widget need multipart-encrypted form |
| 132 | default_outer_class = '' |
132 | 133 | |
133 | | def __init__(self, attrs=None): |
| 134 | def __init__(self, attrs=None, outer_attrs=None): |
134 | 135 | if attrs is not None: |
135 | 136 | self.attrs = attrs.copy() |
136 | 137 | else: |
137 | 138 | self.attrs = {} |
| 139 | if outer_attrs is not None: |
| 140 | self.outer_attrs = outer_attrs.copy() |
| 141 | else: |
| 142 | self.outer_attrs = {} |
| 143 | if 'class' not in self.outer_attrs: |
| 144 | self.outer_attrs['class'] = self.default_outer_class |
138 | 145 | |
139 | 146 | def __deepcopy__(self, memo): |
140 | 147 | obj = copy.copy(self) |
141 | 148 | obj.attrs = self.attrs.copy() |
| 149 | obj.outer_attrs = self.outer_attrs.copy() |
142 | 150 | memo[id(self)] = obj |
143 | 151 | return obj |
144 | 152 | |
… |
… |
|
193 | 201 | |
194 | 202 | class TextInput(Input): |
195 | 203 | input_type = 'text' |
| 204 | default_outer_class = 'text' |
196 | 205 | |
197 | 206 | class PasswordInput(Input): |
198 | 207 | input_type = 'password' |
| 208 | default_outer_class = 'password' |
199 | 209 | |
200 | | def __init__(self, attrs=None, render_value=True): |
201 | | super(PasswordInput, self).__init__(attrs) |
| 210 | def __init__(self, attrs=None, outer_attrs=None, render_value=True): |
| 211 | super(PasswordInput, self).__init__(attrs, outer_attrs) |
202 | 212 | self.render_value = render_value |
203 | 213 | |
204 | 214 | def render(self, name, value, attrs=None): |
… |
… |
|
208 | 218 | class HiddenInput(Input): |
209 | 219 | input_type = 'hidden' |
210 | 220 | is_hidden = True |
| 221 | default_outer_class = 'hidden' |
| 222 | |
211 | 223 | |
212 | 224 | class MultipleHiddenInput(HiddenInput): |
213 | 225 | """ |
214 | 226 | A widget that handles <input type="hidden"> for fields that have a list |
215 | 227 | of values. |
216 | 228 | """ |
217 | | def __init__(self, attrs=None, choices=()): |
218 | | super(MultipleHiddenInput, self).__init__(attrs) |
| 229 | default_outer_class = 'multiply-hidden' |
| 230 | def __init__(self, attrs=None, outer_attrs=None, choices=()): |
| 231 | super(MultipleHiddenInput, self).__init__(attrs, outer_attrs) |
219 | 232 | # choices can be any iterable |
220 | 233 | self.choices = choices |
221 | 234 | |
… |
… |
|
232 | 245 | class FileInput(Input): |
233 | 246 | input_type = 'file' |
234 | 247 | needs_multipart_form = True |
| 248 | default_outer_class = 'file' |
235 | 249 | |
236 | 250 | def render(self, name, value, attrs=None): |
237 | 251 | return super(FileInput, self).render(name, None, attrs=attrs) |
… |
… |
|
241 | 255 | return files.get(name, None) |
242 | 256 | |
243 | 257 | class Textarea(Widget): |
244 | | def __init__(self, attrs=None): |
| 258 | default_outer_class = 'textarea' |
| 259 | def __init__(self, attrs=None, outer_attrs=None): |
245 | 260 | # The 'rows' and 'cols' attributes are required for HTML correctness. |
246 | | self.attrs = {'cols': '40', 'rows': '10'} |
| 261 | default_attrs = {'cols': '40', 'rows': '10'} |
247 | 262 | if attrs: |
248 | | self.attrs.update(attrs) |
| 263 | default_attrs.update(attrs) |
| 264 | super(Textarea, self, default_attrs, outer_attrs) |
249 | 265 | |
250 | 266 | def render(self, name, value, attrs=None): |
251 | 267 | if value is None: value = '' |
… |
… |
|
255 | 271 | |
256 | 272 | class DateTimeInput(Input): |
257 | 273 | input_type = 'text' |
| 274 | default_outer_class = 'date-time' |
258 | 275 | format = '%Y-%m-%d %H:%M:%S' # '2006-10-25 14:30:59' |
259 | 276 | |
260 | | def __init__(self, attrs=None, format=None): |
261 | | super(DateTimeInput, self).__init__(attrs) |
| 277 | def __init__(self, attrs=None, outer_attrs=None, format=None): |
| 278 | super(DateTimeInput, self).__init__(attrs, outer_attrs) |
262 | 279 | if format: |
263 | 280 | self.format = format |
264 | 281 | |
… |
… |
|
270 | 287 | return super(DateTimeInput, self).render(name, value, attrs) |
271 | 288 | |
272 | 289 | class CheckboxInput(Widget): |
273 | | def __init__(self, attrs=None, check_test=bool): |
274 | | super(CheckboxInput, self).__init__(attrs) |
| 290 | default_outer_class = 'checkbox' |
| 291 | def __init__(self, attrs=None, outer_attrs=None, check_test=bool): |
| 292 | super(CheckboxInput, self).__init__(attrs, outer_attrs) |
275 | 293 | # check_test is a callable that takes a value and returns True |
276 | 294 | # if the checkbox should be checked for that value. |
277 | 295 | self.check_test = check_test |
… |
… |
|
296 | 314 | return super(CheckboxInput, self).value_from_datadict(data, files, name) |
297 | 315 | |
298 | 316 | class Select(Widget): |
299 | | def __init__(self, attrs=None, choices=()): |
300 | | super(Select, self).__init__(attrs) |
| 317 | default_outer_class = 'select' |
| 318 | def __init__(self, attrs=None, outer_attrs=None, choices=()): |
| 319 | super(Select, self).__init__(attrs, outer_attrs) |
301 | 320 | # choices can be any iterable, but we may need to render this widget |
302 | 321 | # multiple times. Thus, collapse it into a list so it can be consumed |
303 | 322 | # more than once. |
… |
… |
|
316 | 335 | return u'\n'.join(output) |
317 | 336 | |
318 | 337 | class NullBooleanSelect(Select): |
| 338 | default_outer_class = 'null-boolean-select' |
319 | 339 | """ |
320 | 340 | A Select Widget intended to be used with NullBooleanField. |
321 | 341 | """ |
322 | | def __init__(self, attrs=None): |
| 342 | def __init__(self, attrs=None, outer_attrs=None): |
323 | 343 | choices = ((u'1', ugettext('Unknown')), (u'2', ugettext('Yes')), (u'3', ugettext('No'))) |
324 | | super(NullBooleanSelect, self).__init__(attrs, choices) |
| 344 | super(NullBooleanSelect, self).__init__(attrs, outer_attrs, choices) |
325 | 345 | |
326 | 346 | def render(self, name, value, attrs=None, choices=()): |
327 | 347 | try: |
… |
… |
|
352 | 372 | output.append(u'</select>') |
353 | 373 | return u'\n'.join(output) |
354 | 374 | |
| 375 | default_outer_class = 'select-multiply' |
| 376 | |
355 | 377 | def value_from_datadict(self, data, files, name): |
356 | 378 | if isinstance(data, MultiValueDict): |
357 | 379 | return data.getlist(name) |
… |
… |
|
362 | 384 | An object used by RadioFieldRenderer that represents a single |
363 | 385 | <input type='radio'>. |
364 | 386 | """ |
365 | | |
| 387 | default_outer_class = 'radio' |
366 | 388 | def __init__(self, name, value, attrs, choice, index): |
367 | 389 | self.name, self.value = name, value |
368 | 390 | self.attrs = attrs |
… |
… |
|
409 | 431 | return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]) |
410 | 432 | |
411 | 433 | class RadioSelect(Select): |
412 | | |
| 434 | default_outer_class = 'radio-select' |
413 | 435 | def __init__(self, *args, **kwargs): |
414 | 436 | self.renderer = kwargs.pop('renderer', None) |
415 | 437 | if not self.renderer: |
… |
… |
|
438 | 460 | id_for_label = classmethod(id_for_label) |
439 | 461 | |
440 | 462 | class CheckboxSelectMultiple(SelectMultiple): |
| 463 | default_outer_class = 'checkbox-select-multiply' |
441 | 464 | def render(self, name, value, attrs=None, choices=()): |
442 | 465 | if value is None: value = [] |
443 | 466 | has_id = attrs and 'id' in attrs |
… |
… |
|
490 | 513 | |
491 | 514 | You'll probably want to use this class with MultiValueField. |
492 | 515 | """ |
493 | | def __init__(self, widgets, attrs=None): |
| 516 | def __init__(self, widgets, attrs=None, outer_attrs=None): |
494 | 517 | self.widgets = [isinstance(w, type) and w() or w for w in widgets] |
495 | | super(MultiWidget, self).__init__(attrs) |
| 518 | super(MultiWidget, self).__init__(attrs, outer_attrs) |
496 | 519 | |
497 | 520 | def render(self, name, value, attrs=None): |
498 | 521 | # value is a list of values, each corresponding to a widget |
… |
… |
|
552 | 575 | """ |
553 | 576 | A Widget that splits datetime input into two <input type="text"> boxes. |
554 | 577 | """ |
555 | | def __init__(self, attrs=None): |
| 578 | default_outer_class = 'split-date-time' |
| 579 | def __init__(self, attrs=None, outer_attrs=None): |
556 | 580 | widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs)) |
557 | | super(SplitDateTimeWidget, self).__init__(widgets, attrs) |
| 581 | super(SplitDateTimeWidget, self).__init__(widgets, attrs, outer_attrs) |
558 | 582 | |
559 | 583 | def decompress(self, value): |
560 | 584 | if value: |