=== modified file 'django/newforms/forms.py'
|
|
|
8 | 8 | from django.utils.html import conditional_escape |
9 | 9 | from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode |
10 | 10 | from django.utils.safestring import mark_safe |
| 11 | from django.utils.text import capfirst |
11 | 12 | |
12 | 13 | from fields import Field, FileField |
13 | 14 | from widgets import TextInput, Textarea |
… |
… |
|
24 | 25 | |
25 | 26 | class FormOptions(InheritableOptions): |
26 | 27 | _default_options = { |
| 28 | # main options |
27 | 29 | 'fieldsets': None, |
28 | 30 | 'fields': None, |
29 | 31 | 'exclude': None, |
| 32 | # other options |
| 33 | 'error_class': ErrorList, |
| 34 | 'formfield_for_formfield': lambda self, formfield: formfield, |
| 35 | 'html_class_for_fields_with_errors': 'errors', |
| 36 | 'html_class_for_hidden_fields_row': 'hidden', |
| 37 | 'html_class_for_required_fields': 'required', |
| 38 | 'label_capitalization': True, |
| 39 | 'label_suffix': ':', |
| 40 | 'validation_order': None, |
30 | 41 | } |
31 | 42 | |
32 | 43 | class FormMetaclass(type): |
… |
… |
|
73 | 84 | names = [name for name in new_cls._base_fields_pool if name not in new_cls._meta.exclude] |
74 | 85 | else: |
75 | 86 | names = new_cls._base_fields_pool.keys() |
76 | | new_cls.base_fields = SortedDict([(name, new_cls._base_fields_pool[name]) for name in names]) |
| 87 | new_cls.base_fields = SortedDict([(name, new_cls._meta.formfield_for_formfield(new_cls._meta, new_cls._base_fields_pool[name])) for name in names]) |
77 | 88 | create_base_fields_from_base_fields_pool = classmethod(create_base_fields_from_base_fields_pool) |
78 | 89 | |
79 | 90 | def __new__(cls, name, bases, attrs): |
… |
… |
|
89 | 100 | # class is different than Form. See the comments by the Form class for more |
90 | 101 | # information. Any improvements to the form API should be made to *this* |
91 | 102 | # class, not to the Form class. |
92 | | def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, |
93 | | initial=None, error_class=ErrorList, label_suffix=':'): |
| 103 | def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None): |
94 | 104 | self.is_bound = data is not None or files is not None |
95 | 105 | self.data = data or {} |
96 | 106 | self.files = files or {} |
97 | 107 | self.auto_id = auto_id |
98 | 108 | self.prefix = prefix |
99 | 109 | self.initial = initial or {} |
100 | | self.error_class = error_class |
101 | | self.label_suffix = label_suffix |
102 | 110 | self._errors = None # Stores the errors after clean() has been called. |
103 | 111 | |
104 | 112 | # The base_fields class attribute is the *class-wide* definition of |
… |
… |
|
186 | 194 | |
187 | 195 | def hidden_fields_html_output(self, hidden_fields, hidden_fields_row): |
188 | 196 | "Helper function for outputting HTML from a hidden fields row. Used by _html_output." |
189 | | return hidden_fields_row % u''.join(hidden_fields) |
| 197 | if self._meta.html_class_for_hidden_fields_row: |
| 198 | attrs = u' class="%s"' % self._meta.html_class_for_hidden_fields_row |
| 199 | else: |
| 200 | attrs = u'' |
| 201 | return hidden_fields_row % (attrs, u''.join(hidden_fields)) |
190 | 202 | |
191 | 203 | def _html_output(self, output_type, top_errors_row, fieldset_start, fieldset_end, hidden_fields_row): |
192 | 204 | "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." |
… |
… |
|
221 | 233 | |
222 | 234 | def as_table(self): |
223 | 235 | "Returns this form rendered as HTML <tr>s -- excluding the <table></table>." |
224 | | return self._html_output('table', u'<tr><td colspan="2">%s</td></tr>', u'%s\n<table>', u'</table>\n%s', u'<tr><td colspan="2">%s</td></tr>') |
| 236 | return self._html_output('table', u'<tr><td colspan="2">%s</td></tr>', u'%s\n<table>', u'</table>\n%s', u'<tr%s><td colspan="2">%s</td></tr>') |
225 | 237 | |
226 | 238 | def as_ul(self): |
227 | 239 | "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." |
228 | | return self._html_output('ul', u'<li>%s</li>', u'%s\n<ul>', u'</ul>\n%s', u'<li>%s</li>') |
| 240 | return self._html_output('ul', u'<li>%s</li>', u'%s\n<ul>', u'</ul>\n%s', u'<li%s>%s</li>') |
229 | 241 | |
230 | 242 | def as_p(self): |
231 | 243 | "Returns this form rendered as HTML <p>s." |
232 | | return self._html_output('p', u'%s', u'%s', u'%s', u'<p>%s</p>') |
| 244 | return self._html_output('p', u'%s', u'%s', u'%s', u'<p%s>%s</p>') |
233 | 245 | |
234 | 246 | def non_field_errors(self): |
235 | 247 | """ |
… |
… |
|
237 | 249 | field -- i.e., from Form.clean(). Returns an empty ErrorList if there |
238 | 250 | are none. |
239 | 251 | """ |
240 | | return self.errors.get(NON_FIELD_ERRORS, self.error_class()) |
| 252 | return self.errors.get(NON_FIELD_ERRORS, self._meta.error_class()) |
241 | 253 | |
242 | 254 | def full_clean(self): |
243 | 255 | """ |
… |
… |
|
248 | 260 | if not self.is_bound: # Stop further processing. |
249 | 261 | return |
250 | 262 | self.cleaned_data = {} |
251 | | for name, field in self.fields.items(): |
| 263 | if self._meta.validation_order: |
| 264 | items = [(name, self.fields[name]) for name in self._meta.validation_order] |
| 265 | else: |
| 266 | items = self.fields.items() |
| 267 | for name, field in items: |
252 | 268 | # value_from_datadict() gets the data from the data dictionaries. |
253 | 269 | # Each widget type knows how to retrieve its own data, because some |
254 | 270 | # widgets split data over several HTML fields. |
… |
… |
|
264 | 280 | value = getattr(self, 'clean_%s' % name)() |
265 | 281 | self.cleaned_data[name] = value |
266 | 282 | except ValidationError, e: |
267 | | self._errors[name] = self.error_class(e.messages) |
| 283 | self._errors[name] = self._meta.error_class(e.messages) |
268 | 284 | if name in self.cleaned_data: |
269 | 285 | del self.cleaned_data[name] |
270 | 286 | try: |
271 | 287 | self.cleaned_data = self.clean() |
272 | 288 | except ValidationError, e: |
273 | | self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages) |
| 289 | self._errors[NON_FIELD_ERRORS] = self._meta.error_class(e.messages) |
274 | 290 | if self._errors: |
275 | 291 | delattr(self, 'cleaned_data') |
276 | 292 | |
… |
… |
|
321 | 337 | Returns an ErrorList for this field. Returns an empty ErrorList |
322 | 338 | if there are none. |
323 | 339 | """ |
324 | | return self.form.errors.get(self.name, self.form.error_class()) |
| 340 | return self.form.errors.get(self.name, self.form._meta.error_class()) |
325 | 341 | errors = property(_errors) |
326 | 342 | |
327 | 343 | def as_widget(self, widget=None, attrs=None): |
… |
… |
|
372 | 388 | if self.field.label is None: |
373 | 389 | return pretty_name(self.name) |
374 | 390 | else: |
375 | | return conditional_escape(force_unicode(self.field.label)) |
| 391 | label = conditional_escape(force_unicode(self.field.label)) |
| 392 | if self.form._meta.label_capitalization: |
| 393 | label = capfirst(label) |
| 394 | return label |
376 | 395 | label = property(_label) |
377 | 396 | |
378 | 397 | def _label_tag(self): |
379 | 398 | "Returns label tag for this field as safe HTML code." |
380 | 399 | label = self.label |
381 | | if self.form.label_suffix: |
| 400 | if self.form._meta.label_suffix: |
382 | 401 | # Only add the suffix if the label does not end in punctuation. |
383 | 402 | if label and label[-1] not in ':?.!': |
384 | | label += self.form.label_suffix |
| 403 | label += self.form._meta.label_suffix |
385 | 404 | id_ = self.widget.attrs.get('id') or self.auto_id |
386 | 405 | if label and id_: |
387 | 406 | id_ = self.widget.id_for_label(id_) |
… |
… |
|
400 | 419 | |
401 | 420 | def _row_attrs(self): |
402 | 421 | "Returns row attributes for this field as safe HTML code." |
403 | | return flatatt(self.widget.row_attrs) |
| 422 | attrs = self.widget.row_attrs.copy() |
| 423 | class_list = attrs.pop('class', '').split() |
| 424 | if self.form._meta.html_class_for_fields_with_errors and self.errors: |
| 425 | class_list.append(self.form._meta.html_class_for_fields_with_errors) |
| 426 | if self.form._meta.html_class_for_required_fields and self.field.required: |
| 427 | class_list.append(self.form._meta.html_class_for_required_fields) |
| 428 | if class_list: |
| 429 | attrs['class'] = u' '.join(class_list) |
| 430 | return flatatt(attrs) |
404 | 431 | row_attrs = property(_row_attrs) |
405 | 432 | |
406 | 433 | def _is_hidden(self): |
=== modified file 'django/newforms/models.py'
|
|
|
9 | 9 | from django.utils.encoding import smart_unicode |
10 | 10 | from django.utils.datastructures import SortedDict, InheritableOptions |
11 | 11 | |
12 | | from util import ValidationError, ErrorList |
| 12 | from util import ValidationError |
13 | 13 | from forms import FormOptions, FormMetaclass, BaseForm |
14 | 14 | from fields import Field, ChoiceField, EMPTY_VALUES |
15 | 15 | from widgets import Select, SelectMultiple, MultipleHiddenInput |
… |
… |
|
252 | 252 | return new_cls |
253 | 253 | |
254 | 254 | class BaseModelForm(BaseForm): |
255 | | def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, |
256 | | initial=None, error_class=ErrorList, label_suffix=':', |
257 | | instance=None): |
| 255 | def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, instance=None): |
258 | 256 | opts = self._meta |
259 | 257 | if instance is None: |
260 | 258 | # if we didn't get an instance, instantiate a new one |
… |
… |
|
266 | 264 | # if initial was provided, it should override the values from instance |
267 | 265 | if initial is not None: |
268 | 266 | object_data.update(initial) |
269 | | BaseForm.__init__(self, data, files, auto_id, prefix, object_data, error_class, label_suffix) |
| 267 | BaseForm.__init__(self, data, files, auto_id, prefix, object_data) |
270 | 268 | |
271 | 269 | def save(self, commit=True): |
272 | 270 | """ |