Ticket #12960: 12960.diff

File 12960.diff, 4.7 KB (added by jkocherhans, 5 years ago)

Complete patch. Still thinking about it, but this moves model validation to happen entirely after form validation. I think this is a good thing, but I'm not entirely sure yet.

  • django/forms/forms.py

    diff --git a/django/forms/forms.py b/django/forms/forms.py
    index 6f7a8ce..45bbbb4 100644
    a b class BaseForm(StrAndUnicode): 
    265265            return
    266266        self._clean_fields()
    267267        self._clean_form()
     268        self._post_clean()
    268269        if self._errors:
    269270            delattr(self, 'cleaned_data')
    270271
    class BaseForm(StrAndUnicode): 
    295296        except ValidationError, e:
    296297            self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages)
    297298
     299    def _post_clean(self):
     300        """
     301        An internal hook for performing additional cleaning after form cleaning
     302        is complete. Used for model validation in model forms.
     303        """
     304        pass
     305
    298306    def clean(self):
    299307        """
    300308        Hook for doing any extra form-wide cleaning after Field.clean() been
  • django/forms/models.py

    diff --git a/django/forms/models.py b/django/forms/models.py
    index 65fe1a7..3c95b4d 100644
    a b class BaseModelForm(BaseForm): 
    245245        # if initial was provided, it should override the values from instance
    246246        if initial is not None:
    247247            object_data.update(initial)
     248        # This will be set to True by BaseModelForm.clean(). This makes it so
     249        # it will be set to False if someone overrides that method and doesn't
     250        # call super. Unfortunately the implementation detail of overriding
     251        # clean() to prevent unique valiation was documented.
     252        self._validate_unique = False
    248253        super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data,
    249254                                            error_class, label_suffix, empty_permitted)
    250255
    class BaseModelForm(BaseForm): 
    299304        return exclude
    300305
    301306    def clean(self):
    302         self.validate_unique()
     307        # Set self._validate_unique to True so self.validate_unique will be
     308        # called later, and so that overriding this method and failing to call
     309        # super will cause self.validate_unique() to not be called. This is an
     310        # unfortunate hack to keep backwards compatibility for an
     311        # implementation detail that shouldn't have been documented.
     312        self._validate_unique = True
    303313        return self.cleaned_data
    304314
    305     def _clean_fields(self):
    306         """
    307         Cleans the form fields, constructs the instance, then cleans the model
    308         fields.
    309         """
    310         super(BaseModelForm, self)._clean_fields()
     315    def _post_clean(self):
     316        exclude = self._get_validation_exclusions()
    311317        opts = self._meta
     318
     319        # Update the model instance with self.cleaned_data.
    312320        self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude)
    313         exclude = self._get_validation_exclusions()
     321
     322        # Clean the model instance's fields.
    314323        try:
    315324            self.instance.clean_fields(exclude=exclude)
    316325        except ValidationError, e:
    317326            self._update_errors(e.message_dict)
    318327
    319     def _clean_form(self):
    320         """
    321         Runs the instance's clean method, then the form's. This is becuase the
    322         form will run validate_unique() by default, and we should run the
    323         model's clean method first.
    324         """
     328        # Call the model instance's clean method.
    325329        try:
    326330            self.instance.clean()
    327331        except ValidationError, e:
    328332            self._update_errors({NON_FIELD_ERRORS: e.messages})
    329         super(BaseModelForm, self)._clean_form()
     333
     334        # Validate uniqueness if needed.
     335        if self._validate_unique:
     336            self.validate_unique()
    330337
    331338    def validate_unique(self):
    332339        """
  • tests/regressiontests/model_forms_regress/tests.py

    diff --git a/tests/regressiontests/model_forms_regress/tests.py b/tests/regressiontests/model_forms_regress/tests.py
    index d03e61d..485160a 100644
    a b class OverrideCleanTests(TestCase): 
    7272        # by form.full_clean().
    7373        self.assertEquals(form.instance.left, 1)
    7474
     75# Regression test for #12960.
     76# Make sure the cleaned_data returned from ModelForm.clean() is applied to the
     77# model instance.
     78
     79class PublicationForm(forms.ModelForm):
     80    def clean(self):
     81        print self.cleaned_data
     82        self.cleaned_data['title'] = self.cleaned_data['title'].upper()
     83        return self.cleaned_data
     84
     85    class Meta:
     86        model = Publication
     87
     88class ModelFormCleanTest(TestCase):
     89    def test_model_form_clean_applies_to_model(self):
     90        data = {'title': 'test', 'date_published': '2010-2-25'}
     91        form = PublicationForm(data)
     92        publication = form.save()
     93        self.assertEqual(publication.title, 'TEST')
     94
    7595class FPForm(forms.ModelForm):
    7696    class Meta:
    7797        model = FilePathModel
Back to Top