mirror of
https://github.com/django/django.git
synced 2025-07-07 19:29:12 +00:00
[soc2009/model-validation] Added clean() and validate() methods to Model.
This also called for ValidationError to also accept dicts as message. git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@10869 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
b59bfbd128
commit
6152164303
@ -32,23 +32,33 @@ class FieldError(Exception):
|
|||||||
"""Some kind of problem with a model field."""
|
"""Some kind of problem with a model field."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
NON_FIELD_ERRORS = '__all__'
|
||||||
class ValidationError(Exception):
|
class ValidationError(Exception):
|
||||||
"""An error while validating data."""
|
"""An error while validating data."""
|
||||||
def __init__(self, message):
|
def __init__(self, message):
|
||||||
|
import operator
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_unicode
|
||||||
"""
|
"""
|
||||||
ValidationError can be passed any object that can be printed (usually
|
ValidationError can be passed any object that can be printed (usually
|
||||||
a string) or a list of objects.
|
a string), a list of objects or a dictionary.
|
||||||
"""
|
"""
|
||||||
|
if isinstance(message, dict):
|
||||||
|
self.message_dict = message
|
||||||
|
message = reduce(operator.add, message.values())
|
||||||
|
|
||||||
if isinstance(message, list):
|
if isinstance(message, list):
|
||||||
self.messages = [force_unicode(msg) for msg in message]
|
self.messages = [force_unicode(msg) for msg in message]
|
||||||
else:
|
else:
|
||||||
message = force_unicode(message)
|
message = force_unicode(message)
|
||||||
self.messages = [message]
|
self.messages = [message]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# This is needed because, without a __str__(), printing an exception
|
# This is needed because, without a __str__(), printing an exception
|
||||||
# instance would result in this:
|
# instance would result in this:
|
||||||
# AttributeError: ValidationError instance has no attribute 'args'
|
# AttributeError: ValidationError instance has no attribute 'args'
|
||||||
# See http://www.python.org/doc/current/tut/node10.html#handling
|
# See http://www.python.org/doc/current/tut/node10.html#handling
|
||||||
|
if hasattr(self, 'message_dict'):
|
||||||
|
return repr(self.message_dict)
|
||||||
return repr(self.messages)
|
return repr(self.messages)
|
||||||
|
@ -9,7 +9,7 @@ except NameError:
|
|||||||
from sets import Set as set # Python 2.3 fallback.
|
from sets import Set as set # Python 2.3 fallback.
|
||||||
|
|
||||||
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
|
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS
|
||||||
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
|
||||||
@ -597,6 +597,36 @@ class Model(object):
|
|||||||
def prepare_database_save(self, unused):
|
def prepare_database_save(self, unused):
|
||||||
return self.pk
|
return self.pk
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
"""
|
||||||
|
Hook for doing any extra model-wide validation after Model.clean() been
|
||||||
|
called on every field. Any ValidationError raised by this method will
|
||||||
|
not be associated with a particular field; it will have a special-case
|
||||||
|
association with the field named '__all__'.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
"""
|
||||||
|
Cleans all fields and raises ValidationError containing message_dict of
|
||||||
|
all validation errors if any occur.
|
||||||
|
"""
|
||||||
|
errors = {}
|
||||||
|
for f in self._meta.fields:
|
||||||
|
try:
|
||||||
|
# TODO: is the [sg]etattr correct?
|
||||||
|
setattr(self, f.attname, f.clean(getattr(self, f.attname), self))
|
||||||
|
except ValidationError, e:
|
||||||
|
errors[f.name] = e.messages
|
||||||
|
try:
|
||||||
|
# TODO: run this only if not errors??
|
||||||
|
self.validate()
|
||||||
|
except ValidationError, e:
|
||||||
|
errors[NON_FIELD_ERRORS] = e.messages
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
|
# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
|
||||||
|
0
tests/modeltests/validation/__init__.py
Normal file
0
tests/modeltests/validation/__init__.py
Normal file
28
tests/modeltests/validation/models.py
Normal file
28
tests/modeltests/validation/models.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.db import models
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
class ModelToValidate(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
created = models.DateTimeField(default=datetime.now)
|
||||||
|
number = models.IntegerField()
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
super(ModelToValidate, self).validate()
|
||||||
|
if self.number == 11:
|
||||||
|
raise ValidationError('Invalid number supplied!')
|
||||||
|
|
||||||
|
class BaseModelValidationTests(TestCase):
|
||||||
|
def test_missing_required_field_raises_error(self):
|
||||||
|
mtv = ModelToValidate()
|
||||||
|
self.assertRaises(ValidationError, mtv.clean)
|
||||||
|
|
||||||
|
def test_with_correct_value_model_validates(self):
|
||||||
|
mtv = ModelToValidate(number=10)
|
||||||
|
self.assertEqual(None, mtv.clean())
|
||||||
|
|
||||||
|
def test_custom_validate_method_is_called(self):
|
||||||
|
mtv = ModelToValidate(number=11)
|
||||||
|
self.assertRaises(ValidationError, mtv.clean)
|
Loading…
x
Reference in New Issue
Block a user