1
0
mirror of https://github.com/django/django.git synced 2025-07-07 11:19:12 +00:00

[soc2009/model-validation] Added capacity for ComplexValidator handling to models

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@11271 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Honza Král 2009-07-19 20:55:58 +00:00
parent 021ad1a024
commit 8d4417c75d
5 changed files with 56 additions and 2 deletions

View File

@ -10,6 +10,7 @@ except NameError:
import django.db.models.manager # Imported to register signal handler. import django.db.models.manager # Imported to register signal handler.
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS
from django.core import validators
from django.db.models.fields import AutoField, FieldDoesNotExist from django.db.models.fields import AutoField, FieldDoesNotExist
from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField
from django.db.models.query import delete_objects, Q from django.db.models.query import delete_objects, Q
@ -760,6 +761,20 @@ class Model(object):
setattr(self, f.attname, f.clean(getattr(self, f.attname), self)) setattr(self, f.attname, f.clean(getattr(self, f.attname), self))
except ValidationError, e: except ValidationError, e:
errors[f.name] = e.messages errors[f.name] = e.messages
# run complex validators after the fields have been cleaned since they
# need access to model_instance.
for f in self._meta.fields:
if f.name in errors:
continue
value = getattr(self, f.attname)
for v in f.validators:
if isinstance(v, validators.ComplexValidator):
try:
v(value, obj=self)
except ValidationError, e:
errors.setdefault(f.name, []).extend(e.messages)
try: try:
# TODO: run this only if not errors?? # TODO: run this only if not errors??
self.validate() self.validate()

View File

@ -10,4 +10,12 @@ class ValidationTestCase(unittest.TestCase):
except ValidationError, e: except ValidationError, e:
self.assertEquals(sorted(failed_fields), sorted(e.message_dict.keys())) self.assertEquals(sorted(failed_fields), sorted(e.message_dict.keys()))
def assertFieldFailsValidationWithMessage(self, clean, field_name, message):
self.assertRaises(ValidationError, clean)
try:
clean()
except ValidationError, e:
self.assertTrue(field_name in e.message_dict)
self.assertEquals(message, e.message_dict[field_name])

View File

@ -1,6 +1,7 @@
from datetime import datetime from datetime import datetime
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import ComplexValidator
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase
@ -8,13 +9,21 @@ def validate_answer_to_universe(value):
if value != 42: if value != 42:
raise ValidationError('This is not the answer to life, universe and everything!') raise ValidationError('This is not the answer to life, universe and everything!')
class ValidateFieldNotEqualsOtherField(ComplexValidator):
def __init__(self, other_field):
self.other_field = other_field
def __call__(self, value, all_values={}, obj=None):
if value == self.get_value(self.other_field, all_values, obj):
raise ValidationError("Must not equal to %r's value" % self.other_field)
class ModelToValidate(models.Model): class ModelToValidate(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
created = models.DateTimeField(default=datetime.now) created = models.DateTimeField(default=datetime.now)
number = models.IntegerField() number = models.IntegerField()
parent = models.ForeignKey('self', blank=True, null=True) parent = models.ForeignKey('self', blank=True, null=True)
email = models.EmailField(blank=True) email = models.EmailField(blank=True)
f_with_custom_validator = models.IntegerField(blank=True, null=True, validators=[validate_answer_to_universe]) f_with_custom_validator = models.IntegerField(blank=True, null=True, validators=[validate_answer_to_universe, ValidateFieldNotEqualsOtherField('number')])
def validate(self): def validate(self):
super(ModelToValidate, self).validate() super(ModelToValidate, self).validate()

View File

@ -11,7 +11,7 @@ from validators import TestModelsWithValidators
class BaseModelValidationTests(ValidationTestCase): class BaseModelValidationTests(ValidationTestCase):
def test_missing_required_field_raises_error(self): def test_missing_required_field_raises_error(self):
mtv = ModelToValidate() mtv = ModelToValidate(f_with_custom_validator=42)
self.assertFailsValidation(mtv.clean, ['name', 'number']) self.assertFailsValidation(mtv.clean, ['name', 'number'])
def test_with_correct_value_model_validates(self): def test_with_correct_value_model_validates(self):

View File

@ -11,4 +11,26 @@ class TestModelsWithValidators(ValidationTestCase):
def test_custom_validator_raises_error_for_incorrect_value(self): def test_custom_validator_raises_error_for_incorrect_value(self):
mtv = ModelToValidate(number=10, name='Some Name', f_with_custom_validator=12) mtv = ModelToValidate(number=10, name='Some Name', f_with_custom_validator=12)
self.assertFailsValidation(mtv.clean, ['f_with_custom_validator']) self.assertFailsValidation(mtv.clean, ['f_with_custom_validator'])
self.assertFieldFailsValidationWithMessage(
mtv.clean,
'f_with_custom_validator',
[u'This is not the answer to life, universe and everything!']
)
def test_custom_complex_validator_raises_error_for_incorrect_value(self):
mtv = ModelToValidate(number=42, name='Some Name', f_with_custom_validator=42)
self.assertFailsValidation(mtv.clean, ['f_with_custom_validator'])
self.assertFieldFailsValidationWithMessage(
mtv.clean,
'f_with_custom_validator',
[u"Must not equal to 'number''s value"]
)
def test_complex_validator_isnt_run_if_field_doesnt_clean(self):
mtv = ModelToValidate(number=32, name='Some Name', f_with_custom_validator=32)
self.assertFieldFailsValidationWithMessage(
mtv.clean,
'f_with_custom_validator',
[u'This is not the answer to life, universe and everything!']
)