diff --git a/django/forms/forms.py b/django/forms/forms.py
index c53a901..73599d0 100644
a
|
b
|
class BaseForm(StrAndUnicode):
|
263 | 263 | # changed from the initial data, short circuit any validation. |
264 | 264 | if self.empty_permitted and not self.has_changed(): |
265 | 265 | return |
| 266 | |
| 267 | self._clean_fields() |
| 268 | self._clean_form() |
| 269 | |
| 270 | if self._errors: |
| 271 | delattr(self, 'cleaned_data') |
| 272 | |
| 273 | def _clean_fields(self): |
266 | 274 | for name, field in self.fields.items(): |
267 | 275 | # value_from_datadict() gets the data from the data dictionaries. |
268 | 276 | # Each widget type knows how to retrieve its own data, because some |
… |
… |
class BaseForm(StrAndUnicode):
|
282 | 290 | self._errors[name] = self.error_class(e.messages) |
283 | 291 | if name in self.cleaned_data: |
284 | 292 | del self.cleaned_data[name] |
| 293 | |
| 294 | def _clean_form(self): |
285 | 295 | try: |
286 | 296 | self.cleaned_data = self.clean() |
287 | 297 | except ValidationError, e: |
288 | 298 | self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages) |
289 | | if self._errors: |
290 | | delattr(self, 'cleaned_data') |
291 | 299 | |
292 | 300 | def clean(self): |
293 | 301 | """ |
diff --git a/django/forms/models.py b/django/forms/models.py
index cce2319..6aa08f7 100644
a
|
b
|
class BaseModelForm(BaseForm):
|
280 | 280 | exclude.append(f.name) |
281 | 281 | return exclude |
282 | 282 | |
283 | | def clean(self): |
| 283 | def _clean_form(self): |
| 284 | # construct the instance after the form has been validated on the form level. |
| 285 | # this is a partial fix for #12596 which allows people to still use |
| 286 | # ModelForms without calling super().clean() even though they thus |
| 287 | # loose some validation (everything run on models, including validating |
| 288 | # the fields) |
284 | 289 | opts = self._meta |
285 | 290 | self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) |
| 291 | super(BaseModelForm, self)._clean_form() |
| 292 | |
| 293 | def clean(self): |
286 | 294 | exclude = self._get_validation_exclusions() |
287 | 295 | try: |
288 | 296 | self.instance.full_clean(exclude=exclude) |
diff --git a/tests/regressiontests/model_forms_regress/tests.py b/tests/regressiontests/model_forms_regress/tests.py
index f8b6511..57d5655 100644
a
|
b
|
class UniqueTogetherTests(TestCase):
|
50 | 50 | form = TripleForm({'left': '1', 'middle': '3', 'right': '1'}) |
51 | 51 | self.failUnless(form.is_valid()) |
52 | 52 | |
| 53 | class TripleFormWithCleanOverride(forms.ModelForm): |
| 54 | class Meta: |
| 55 | model = Triple |
| 56 | |
| 57 | def clean(self): |
| 58 | if not self.cleaned_data['left'] == self.cleaned_data['right']: |
| 59 | raise forms.ValidationError('Left and right should be equal') |
| 60 | return self.cleaned_data |
| 61 | |
| 62 | class OverrideCleanTests(TestCase): |
| 63 | def test_override_clean(self): |
| 64 | """ |
| 65 | Regression for #12596: Calling super from ModelForm.clean() should be |
| 66 | optional. |
| 67 | """ |
| 68 | form = TripleFormWithCleanOverride({'left': 1, 'middle': 2, 'right': 1}) |
| 69 | self.failUnless(form.is_valid()) |
| 70 | # form.instance.left will be None if the instance was not constructed |
| 71 | # by form.full_clean(). |
| 72 | self.assertEquals(form.instance.left, 1) |
| 73 | |
53 | 74 | class FPForm(forms.ModelForm): |
54 | 75 | class Meta: |
55 | 76 | model = FilePathModel |