summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHonza Král <honza.kral@gmail.com>2009-07-19 20:55:58 +0000
committerHonza Král <honza.kral@gmail.com>2009-07-19 20:55:58 +0000
commit8d4417c75df30502f47abf6bd3582dfa2425f562 (patch)
treeee2fdc4498516ec88b6b97d241e82ca1b8bb9968
parent021ad1a02433f194ecd7776aa0522a7cf3b0d9b8 (diff)
[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
-rw-r--r--django/db/models/base.py15
-rw-r--r--tests/modeltests/validation/__init__.py8
-rw-r--r--tests/modeltests/validation/models.py11
-rw-r--r--tests/modeltests/validation/tests.py2
-rw-r--r--tests/modeltests/validation/validators.py22
5 files changed, 56 insertions, 2 deletions
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 0e1587c033..f4a1c28179 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -10,6 +10,7 @@ except NameError:
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 import validators
from django.db.models.fields import AutoField, FieldDoesNotExist
from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField
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))
except ValidationError, e:
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:
# TODO: run this only if not errors??
self.validate()
diff --git a/tests/modeltests/validation/__init__.py b/tests/modeltests/validation/__init__.py
index 88e0029078..d0a7d19d49 100644
--- a/tests/modeltests/validation/__init__.py
+++ b/tests/modeltests/validation/__init__.py
@@ -9,5 +9,13 @@ class ValidationTestCase(unittest.TestCase):
clean()
except ValidationError, e:
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])
diff --git a/tests/modeltests/validation/models.py b/tests/modeltests/validation/models.py
index e41fc3b332..c00b7f28d2 100644
--- a/tests/modeltests/validation/models.py
+++ b/tests/modeltests/validation/models.py
@@ -1,6 +1,7 @@
from datetime import datetime
from django.core.exceptions import ValidationError
+from django.core.validators import ComplexValidator
from django.db import models
from django.test import TestCase
@@ -8,13 +9,21 @@ def validate_answer_to_universe(value):
if value != 42:
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):
name = models.CharField(max_length=100)
created = models.DateTimeField(default=datetime.now)
number = models.IntegerField()
parent = models.ForeignKey('self', blank=True, null=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):
super(ModelToValidate, self).validate()
diff --git a/tests/modeltests/validation/tests.py b/tests/modeltests/validation/tests.py
index e6d1697ce2..8177c1a2cc 100644
--- a/tests/modeltests/validation/tests.py
+++ b/tests/modeltests/validation/tests.py
@@ -11,7 +11,7 @@ from validators import TestModelsWithValidators
class BaseModelValidationTests(ValidationTestCase):
def test_missing_required_field_raises_error(self):
- mtv = ModelToValidate()
+ mtv = ModelToValidate(f_with_custom_validator=42)
self.assertFailsValidation(mtv.clean, ['name', 'number'])
def test_with_correct_value_model_validates(self):
diff --git a/tests/modeltests/validation/validators.py b/tests/modeltests/validation/validators.py
index 2556785779..ac22afa295 100644
--- a/tests/modeltests/validation/validators.py
+++ b/tests/modeltests/validation/validators.py
@@ -11,4 +11,26 @@ class TestModelsWithValidators(ValidationTestCase):
def test_custom_validator_raises_error_for_incorrect_value(self):
mtv = ModelToValidate(number=10, name='Some Name', f_with_custom_validator=12)
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!']
+ )