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:
parent
021ad1a024
commit
8d4417c75d
@ -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()
|
||||||
|
@ -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])
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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):
|
||||||
|
@ -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!']
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user