diff --git a/django/forms/forms.py b/django/forms/forms.py
index 6f7a8ce..45bbbb4 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 65fe1a7..3c95b4d 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -245,6 +245,11 @@ class BaseModelForm(BaseForm):
         # if initial was provided, it should override the values from instance
         if initial is not None:
             object_data.update(initial)
+        # This will be set to True by BaseModelForm.clean(). This makes it so
+        # it will be set to False if someone overrides that method and doesn't
+        # call super. Unfortunately the implementation detail of overriding
+        # clean() to prevent unique valiation was documented.
+        self._validate_unique = False
         super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data,
                                             error_class, label_suffix, empty_permitted)
 
@@ -299,34 +304,36 @@ class BaseModelForm(BaseForm):
         return exclude
 
     def clean(self):
-        self.validate_unique()
+        # Set self._validate_unique to True so self.validate_unique will be
+        # called later, and so that overriding this method and failing to call
+        # super will cause self.validate_unique() to not be called. This is an
+        # unfortunate hack to keep backwards compatibility for an
+        # implementation detail that shouldn't have been documented.
+        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()
+    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)
-        exclude = self._get_validation_exclusions()
+
+        # 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 d03e61d..485160a 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
