From b88f969789a5d69c770c25e1af9a2f4292184a6f Mon Sep 17 00:00:00 2001 From: Joseph Kocherhans Date: Sat, 6 Mar 2010 18:42:56 +0000 Subject: [PATCH] Fixed #12960. The return value of ModelForm.clean() is now applied to the model. Thanks for the report, krejcik. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12690 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/forms/forms.py | 8 +++++ django/forms/models.py | 33 ++++++++++--------- .../model_forms_regress/tests.py | 20 +++++++++++ 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/django/forms/forms.py b/django/forms/forms.py index 6f7a8ce97d..45bbbb41b6 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -265,6 +265,7 @@ class BaseForm(StrAndUnicode): return self._clean_fields() self._clean_form() + self._post_clean() if self._errors: delattr(self, 'cleaned_data') @@ -295,6 +296,13 @@ class BaseForm(StrAndUnicode): except ValidationError, e: self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages) + def _post_clean(self): + """ + An internal hook for performing additional cleaning after form cleaning + is complete. Used for model validation in model forms. + """ + pass + def clean(self): """ Hook for doing any extra form-wide cleaning after Field.clean() been diff --git a/django/forms/models.py b/django/forms/models.py index 65fe1a7bd4..acfbe490d5 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -245,6 +245,10 @@ class BaseModelForm(BaseForm): # if initial was provided, it should override the values from instance if initial is not None: object_data.update(initial) + # self._validate_unique will be set to True by BaseModelForm.clean(). + # It is False by default so overriding self.clean() and failing to call + # super will stop validate_unique from being called. + self._validate_unique = False super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data, error_class, label_suffix, empty_permitted) @@ -299,34 +303,31 @@ class BaseModelForm(BaseForm): return exclude def clean(self): - self.validate_unique() + self._validate_unique = True return self.cleaned_data - def _clean_fields(self): - """ - Cleans the form fields, constructs the instance, then cleans the model - fields. - """ - super(BaseModelForm, self)._clean_fields() - opts = self._meta - self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) + def _post_clean(self): exclude = self._get_validation_exclusions() + opts = self._meta + + # Update the model instance with self.cleaned_data. + self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) + + # Clean the model instance's fields. try: self.instance.clean_fields(exclude=exclude) except ValidationError, e: self._update_errors(e.message_dict) - def _clean_form(self): - """ - Runs the instance's clean method, then the form's. This is becuase the - form will run validate_unique() by default, and we should run the - model's clean method first. - """ + # Call the model instance's clean method. try: self.instance.clean() except ValidationError, e: self._update_errors({NON_FIELD_ERRORS: e.messages}) - super(BaseModelForm, self)._clean_form() + + # Validate uniqueness if needed. + if self._validate_unique: + self.validate_unique() def validate_unique(self): """ diff --git a/tests/regressiontests/model_forms_regress/tests.py b/tests/regressiontests/model_forms_regress/tests.py index d03e61d4fa..485160a7f2 100644 --- a/tests/regressiontests/model_forms_regress/tests.py +++ b/tests/regressiontests/model_forms_regress/tests.py @@ -72,6 +72,26 @@ class OverrideCleanTests(TestCase): # by form.full_clean(). self.assertEquals(form.instance.left, 1) +# Regression test for #12960. +# Make sure the cleaned_data returned from ModelForm.clean() is applied to the +# model instance. + +class PublicationForm(forms.ModelForm): + def clean(self): + print self.cleaned_data + self.cleaned_data['title'] = self.cleaned_data['title'].upper() + return self.cleaned_data + + class Meta: + model = Publication + +class ModelFormCleanTest(TestCase): + def test_model_form_clean_applies_to_model(self): + data = {'title': 'test', 'date_published': '2010-2-25'} + form = PublicationForm(data) + publication = form.save() + self.assertEqual(publication.title, 'TEST') + class FPForm(forms.ModelForm): class Meta: model = FilePathModel