diff --git a/django/forms/forms.py b/django/forms/forms.py
a
|
b
|
|
253 | 253 | def full_clean(self): |
254 | 254 | """ |
255 | 255 | Cleans all of self.data and populates self._errors and |
256 | | self.cleaned_data. |
| 256 | self.cleaned_data. Returns True if validation was performed, |
| 257 | False otherwise. |
257 | 258 | """ |
258 | 259 | self._errors = ErrorDict() |
259 | 260 | if not self.is_bound: # Stop further processing. |
260 | | return |
| 261 | return False |
261 | 262 | self.cleaned_data = {} |
262 | 263 | # If the form is permitted to be empty, and none of the form data has |
263 | 264 | # changed from the initial data, short circuit any validation. |
264 | 265 | if self.empty_permitted and not self.has_changed(): |
265 | | return |
| 266 | return False |
266 | 267 | for name, field in self.fields.items(): |
267 | 268 | # value_from_datadict() gets the data from the data dictionaries. |
268 | 269 | # Each widget type knows how to retrieve its own data, because some |
… |
… |
|
288 | 289 | self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages) |
289 | 290 | if self._errors: |
290 | 291 | delattr(self, 'cleaned_data') |
| 292 | return True |
291 | 293 | |
292 | 294 | def clean(self): |
293 | 295 | """ |
diff --git a/django/forms/models.py b/django/forms/models.py
a
|
b
|
|
280 | 280 | exclude.append(f.name) |
281 | 281 | return exclude |
282 | 282 | |
283 | | def clean(self): |
| 283 | def full_clean(self): |
| 284 | needs_validation = super(BaseModelForm, self).full_clean() |
| 285 | if needs_validation: |
| 286 | self.clean_instance() |
| 287 | return needs_validation |
| 288 | |
| 289 | def clean_instance(self): |
| 290 | if not hasattr(self, 'cleaned_data'): |
| 291 | self.cleaned_data = {} |
284 | 292 | opts = self._meta |
285 | 293 | self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) |
286 | 294 | exclude = self._get_validation_exclusions() |
… |
… |
|
288 | 296 | self.instance.full_clean(exclude=exclude) |
289 | 297 | except ValidationError, e: |
290 | 298 | for k, v in e.message_dict.items(): |
291 | | if k != NON_FIELD_ERRORS: |
292 | | self._errors.setdefault(k, ErrorList()).extend(v) |
293 | | # Remove the data from the cleaned_data dict since it was invalid |
294 | | if k in self.cleaned_data: |
295 | | del self.cleaned_data[k] |
296 | | if NON_FIELD_ERRORS in e.message_dict: |
297 | | raise ValidationError(e.message_dict[NON_FIELD_ERRORS]) |
298 | | return self.cleaned_data |
| 299 | self._errors.setdefault(k, ErrorList()).extend(v) |
| 300 | if self._errors: |
| 301 | delattr(self, 'cleaned_data') |
299 | 302 | |
300 | 303 | def save(self, commit=True): |
301 | 304 | """ |
diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
a
|
b
|
|
464 | 464 | You can override the ``clean()`` method on a model form to provide additional |
465 | 465 | validation in the same way you can on a normal form. |
466 | 466 | |
467 | | In this regard, model forms have two specific characteristics when compared to |
468 | | forms: |
469 | | |
470 | | By default the ``clean()`` method validates the uniqueness of fields that are |
471 | | marked as ``unique``, ``unique_together`` or ``unique_for_date|month|year`` on |
472 | | the model. Therefore, if you would like to override the ``clean()`` method and |
473 | | maintain the default validation, you must call the parent class's ``clean()`` |
474 | | method. |
475 | | |
476 | 467 | Also, a model form instance bound to a model object will contain a |
477 | 468 | ``self.instance`` attribute that gives model form methods access to that |
478 | 469 | specific model instance. |
diff --git a/tests/regressiontests/model_forms_regress/tests.py b/tests/regressiontests/model_forms_regress/tests.py
a
|
b
|
|
50 | 50 | form = TripleForm({'left': '1', 'middle': '3', 'right': '1'}) |
51 | 51 | self.failUnless(form.is_valid()) |
52 | 52 | |
| 53 | |
| 54 | class TripleFormWithCleanOverride(forms.ModelForm): |
| 55 | class Meta: |
| 56 | model = Triple |
| 57 | |
| 58 | def clean(self): |
| 59 | if not self.cleaned_data['left'] == self.cleaned_data['right']: |
| 60 | raise forms.ValidationError('Left and right should be equal') |
| 61 | return self.cleaned_data |
| 62 | |
| 63 | class OverrideCleanTests(TestCase): |
| 64 | def test_override_clean(self): |
| 65 | """ |
| 66 | Regression for #12596: ModelForm.clean() should be optional. |
| 67 | |
| 68 | """ |
| 69 | form = TripleFormWithCleanOverride({'left': 1, 'middle': 2, 'right': 1}) |
| 70 | self.failUnless(form.is_valid()) |
| 71 | self.assertEquals(form.instance.left, 1, |
| 72 | 'form.instance not set up by form.is_valid()') |
| 73 | |
| 74 | |
53 | 75 | class FPForm(forms.ModelForm): |
54 | 76 | class Meta: |
55 | 77 | model = FilePathModel |
… |
… |
|
113 | 135 | date_published=date(1991, 8, 22)) |
114 | 136 | f = Form() |
115 | 137 | self.assertEqual(len(f.fields["publications"].choices), 1) |
| 138 | |