=== modified file 'django/newforms/extras/widgets.py'
|
|
|
20 | 20 | day_field = '%s_day' |
21 | 21 | year_field = '%s_year' |
22 | 22 | |
23 | | def __init__(self, attrs=None, years=None): |
| 23 | def __init__(self, attrs=None, outer_attrs=None, years=None): |
24 | 24 | # years is an optional list/tuple of years to use in the "year" select box. |
25 | | self.attrs = attrs or {} |
| 25 | super(SelectDateWidget, self).__init__(attrs, outer_attrs) |
26 | 26 | if years: |
27 | 27 | self.years = years |
28 | 28 | 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_list = outer_attrs.pop('class', '').split() |
| 136 | if bf.field.required: |
| 137 | outer_class_list.append('required') |
| 138 | if bf_errors: |
| 139 | outer_class_list.append('error') |
| 140 | if outer_class_list: |
| 141 | outer_attrs['class'] = ' '.join(outer_class_list) |
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'
|
|
|
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 | 132 | |
133 | | def __init__(self, attrs=None): |
| 133 | def __init__(self, attrs=None, outer_attrs=None): |
134 | 134 | if attrs is not None: |
135 | 135 | self.attrs = attrs.copy() |
136 | 136 | else: |
137 | 137 | self.attrs = {} |
| 138 | if outer_attrs is not None: |
| 139 | self.outer_attrs = outer_attrs.copy() |
| 140 | else: |
| 141 | self.outer_attrs = {} |
138 | 142 | |
139 | 143 | def __deepcopy__(self, memo): |
140 | 144 | obj = copy.copy(self) |
141 | 145 | obj.attrs = self.attrs.copy() |
| 146 | obj.outer_attrs = self.outer_attrs.copy() |
142 | 147 | memo[id(self)] = obj |
143 | 148 | return obj |
144 | 149 | |
… |
… |
|
197 | 202 | class PasswordInput(Input): |
198 | 203 | input_type = 'password' |
199 | 204 | |
200 | | def __init__(self, attrs=None, render_value=True): |
201 | | super(PasswordInput, self).__init__(attrs) |
| 205 | def __init__(self, attrs=None, outer_attrs=None, render_value=True): |
| 206 | super(PasswordInput, self).__init__(attrs, outer_attrs) |
202 | 207 | self.render_value = render_value |
203 | 208 | |
204 | 209 | def render(self, name, value, attrs=None): |
… |
… |
|
214 | 219 | A widget that handles <input type="hidden"> for fields that have a list |
215 | 220 | of values. |
216 | 221 | """ |
217 | | def __init__(self, attrs=None, choices=()): |
218 | | super(MultipleHiddenInput, self).__init__(attrs) |
| 222 | def __init__(self, attrs=None, outer_attrs=None, choices=()): |
| 223 | super(MultipleHiddenInput, self).__init__(attrs, outer_attrs) |
219 | 224 | # choices can be any iterable |
220 | 225 | self.choices = choices |
221 | 226 | |
… |
… |
|
241 | 246 | return files.get(name, None) |
242 | 247 | |
243 | 248 | class Textarea(Widget): |
244 | | def __init__(self, attrs=None): |
| 249 | def __init__(self, attrs=None, outer_attrs=None): |
245 | 250 | # The 'rows' and 'cols' attributes are required for HTML correctness. |
246 | | self.attrs = {'cols': '40', 'rows': '10'} |
| 251 | default_attrs = {'cols': '40', 'rows': '10'} |
247 | 252 | if attrs: |
248 | | self.attrs.update(attrs) |
| 253 | default_attrs.update(attrs) |
| 254 | super(Textarea, self).__init__(default_attrs, outer_attrs) |
249 | 255 | |
250 | 256 | def render(self, name, value, attrs=None): |
251 | 257 | if value is None: value = '' |
… |
… |
|
257 | 263 | input_type = 'text' |
258 | 264 | format = '%Y-%m-%d %H:%M:%S' # '2006-10-25 14:30:59' |
259 | 265 | |
260 | | def __init__(self, attrs=None, format=None): |
261 | | super(DateTimeInput, self).__init__(attrs) |
| 266 | def __init__(self, attrs=None, outer_attrs=None, format=None): |
| 267 | super(DateTimeInput, self).__init__(attrs, outer_attrs) |
262 | 268 | if format: |
263 | 269 | self.format = format |
264 | 270 | |
… |
… |
|
270 | 276 | return super(DateTimeInput, self).render(name, value, attrs) |
271 | 277 | |
272 | 278 | class CheckboxInput(Widget): |
273 | | def __init__(self, attrs=None, check_test=bool): |
274 | | super(CheckboxInput, self).__init__(attrs) |
| 279 | def __init__(self, attrs=None, outer_attrs=None, check_test=bool): |
| 280 | super(CheckboxInput, self).__init__(attrs, outer_attrs) |
275 | 281 | # check_test is a callable that takes a value and returns True |
276 | 282 | # if the checkbox should be checked for that value. |
277 | 283 | self.check_test = check_test |
… |
… |
|
296 | 302 | return super(CheckboxInput, self).value_from_datadict(data, files, name) |
297 | 303 | |
298 | 304 | class Select(Widget): |
299 | | def __init__(self, attrs=None, choices=()): |
300 | | super(Select, self).__init__(attrs) |
| 305 | def __init__(self, attrs=None, outer_attrs=None, choices=()): |
| 306 | super(Select, self).__init__(attrs, outer_attrs) |
301 | 307 | # choices can be any iterable, but we may need to render this widget |
302 | 308 | # multiple times. Thus, collapse it into a list so it can be consumed |
303 | 309 | # more than once. |
… |
… |
|
319 | 325 | """ |
320 | 326 | A Select Widget intended to be used with NullBooleanField. |
321 | 327 | """ |
322 | | def __init__(self, attrs=None): |
| 328 | def __init__(self, attrs=None, outer_attrs=None): |
323 | 329 | choices = ((u'1', ugettext('Unknown')), (u'2', ugettext('Yes')), (u'3', ugettext('No'))) |
324 | | super(NullBooleanSelect, self).__init__(attrs, choices) |
| 330 | super(NullBooleanSelect, self).__init__(attrs, outer_attrs, choices) |
325 | 331 | |
326 | 332 | def render(self, name, value, attrs=None, choices=()): |
327 | 333 | try: |
… |
… |
|
490 | 496 | |
491 | 497 | You'll probably want to use this class with MultiValueField. |
492 | 498 | """ |
493 | | def __init__(self, widgets, attrs=None): |
| 499 | def __init__(self, widgets, attrs=None, outer_attrs=None): |
494 | 500 | self.widgets = [isinstance(w, type) and w() or w for w in widgets] |
495 | | super(MultiWidget, self).__init__(attrs) |
| 501 | super(MultiWidget, self).__init__(attrs, outer_attrs) |
496 | 502 | |
497 | 503 | def render(self, name, value, attrs=None): |
498 | 504 | # value is a list of values, each corresponding to a widget |
… |
… |
|
552 | 558 | """ |
553 | 559 | A Widget that splits datetime input into two <input type="text"> boxes. |
554 | 560 | """ |
555 | | def __init__(self, attrs=None): |
| 561 | def __init__(self, attrs=None, outer_attrs=None): |
556 | 562 | widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs)) |
557 | | super(SplitDateTimeWidget, self).__init__(widgets, attrs) |
| 563 | super(SplitDateTimeWidget, self).__init__(widgets, attrs, outer_attrs) |
558 | 564 | |
559 | 565 | def decompress(self, value): |
560 | 566 | if value: |