Ticket #16986: model-clean-errordict-4.diff

File model-clean-errordict-4.diff, 7.0 KB (added by David Foster, 8 years ago)
  • docs/releases/1.4.txt

     
    477477  This should make it easier to read when debugging interaction with
    478478  client-side Javascript code.
    479479
     480* :meth:`Model.clean()` can raise :exc:`~django.core.exceptions.ValidationError` with
     481  a dictionary, permitting validation errors on individual fields.
     482
    480483.. _backwards-incompatible-changes-1.4:
    481484
    482485Backwards incompatible changes in 1.4
  • docs/ref/models/instances.txt

     
    115115    except ValidationError, e:
    116116        non_field_errors = e.message_dict[NON_FIELD_ERRORS]
    117117
     118.. versionadded:: 1.4
     119   :meth:`Model.clean()` recognizes :exc:`~django.core.exceptions.ValidationError` with a
     120   dictionary.
     121
     122Alternatively, it is possible to specify errors for individual fields by providing
     123a dictionary to the :exc:`~django.core.exceptions.ValidationError` constructor that maps field names to messages::
     124
     125    def clean(self):
     126        from collections import defaultdict
     127        from django.core.exceptions import ValidationError
     128       
     129        message_dict = defaultdict(list)
     130        if self.min and self.max and (not self.min <= self.max):
     131            message_dict['min'].append(u'Min value must be less that or equal to the max value.')
     132            message_dict['max'].append(u'Max value must be greater than or equal to the min value.')
     133        if len(message_dict):
     134            raise ValidationError(message_dict)
     135
    118136Finally, ``full_clean()`` will check any unique constraints on your model.
    119137
    120138.. method:: Model.validate_unique(exclude=None)
  • django/forms/models.py

     
    331331        try:
    332332            self.instance.clean()
    333333        except ValidationError, e:
    334             self._update_errors({NON_FIELD_ERRORS: e.messages})
     334            if e.has_message_dict:
     335                self._update_errors(e.message_dict)
     336            else:
     337                self._update_errors({NON_FIELD_ERRORS: e.messages})
    335338
    336339        # Validate uniqueness if needed.
    337340        if self._validate_unique:
  • django/core/exceptions.py

     
    5959            self.params = params
    6060            message = force_unicode(message)
    6161            self.messages = [message]
    62 
     62   
     63    @property
     64    def has_message_dict(self):
     65        return hasattr(self, 'message_dict')
     66   
    6367    def __str__(self):
    6468        # This is needed because, without a __str__(), printing an exception
    6569        # instance would result in this:
    6670        # AttributeError: ValidationError instance has no attribute 'args'
    6771        # See http://www.python.org/doc/current/tut/node10.html#handling
    68         if hasattr(self, 'message_dict'):
     72        if self.has_message_dict:
    6973            return repr(self.message_dict)
    7074        return repr(self.messages)
    7175
    7276    def __repr__(self):
    73         if hasattr(self, 'message_dict'):
     77        if self.has_message_dict:
    7478            return 'ValidationError(%s)' % repr(self.message_dict)
    7579        return 'ValidationError(%s)' % repr(self.messages)
    7680
    7781    def update_error_dict(self, error_dict):
    78         if hasattr(self, 'message_dict'):
     82        if self.has_message_dict:
    7983            if error_dict:
    8084                for k, v in self.message_dict.items():
    8185                    error_dict.setdefault(k, []).extend(v)
  • tests/modeltests/validation/tests.py

     
    1111
    1212from . import ValidationTestCase
    1313from .models import (Author, Article, ModelToValidate,
    14     GenericIPAddressTestModel, GenericIPAddrUnpackUniqueTest)
     14    GenericIPAddressTestModel, GenericIPAddrUnpackUniqueTest, Range)
    1515
    1616# Import other tests for this package.
    1717from .test_custom_messages import CustomMessagesTest
     
    196196        giptm.save()
    197197        giptm = GenericIPAddrUnpackUniqueTest(generic_v4unpack_ip="18.52.18.52")
    198198        self.assertFailsValidation(giptm.full_clean, ['generic_v4unpack_ip',])
     199
     200class RangeForm(forms.ModelForm):
     201    class Meta:
     202        model = Range
     203
     204class ModelCleanPerFieldValidationTests(ValidationTestCase):
     205   
     206    def test_no_errors(self):
     207        range = Range(min=0, max=1)
     208        range.full_clean()
     209       
     210        form = RangeForm(instance=range)
     211        form.full_clean()
     212        self.assertEqual({}, form.errors)
     213   
     214    def test_two_errors(self):
     215        range = Range(min=999, max=1)
     216        self.assertFieldFailsValidationWithMessage(range.full_clean, 'min', [u'Min value must be less that or equal to the max value.'])
     217        self.assertFieldFailsValidationWithMessage(range.full_clean, 'max', [u'Max value must be greater than or equal to the min value.'])
     218       
     219        # In particular, the key should not be '__all__'
     220        expected_error_dict = {
     221            'min': [u'Min value must be less that or equal to the max value.'],
     222            'max': [u'Max value must be greater than or equal to the min value.'],
     223        }
     224        form = RangeForm(data={'min': 999, 'max': 1})
     225        form.full_clean()
     226        self.assertEqual(expected_error_dict, form.errors)
  • tests/modeltests/validation/models.py

     
     1from collections import defaultdict
    12from datetime import datetime
    23
    34from django.core.exceptions import ValidationError
     
    9293class GenericIPAddrUnpackUniqueTest(models.Model):
    9394    generic_v4unpack_ip = models.GenericIPAddressField(blank=True, unique=True, unpack_ipv4=True)
    9495
     96class Range(models.Model):
     97    min = models.IntegerField()
     98    max = models.IntegerField()
     99   
     100    def clean(self):
     101        message_dict = defaultdict(list)
     102        if self.min and self.max and (not self.min <= self.max):
     103            message_dict['min'].append(u'Min value must be less that or equal to the max value.')
     104            message_dict['max'].append(u'Max value must be greater than or equal to the min value.')
     105        if len(message_dict):
     106            raise ValidationError(message_dict)
    95107
    96108# A model can't have multiple AutoFields
    97109# Refs #12467.
     
    102114        auto2 = models.AutoField(primary_key=True)
    103115except AssertionError, assertion_error:
    104116    pass # Fail silently
    105 assert str(assertion_error) == u"A model can't have more than one AutoField."
    106  No newline at end of file
     117assert str(assertion_error) == u"A model can't have more than one AutoField."
Back to Top