1
0
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:
Honza Král 2009-06-01 15:38:58 +00:00
parent b59bfbd128
commit 6152164303
4 changed files with 70 additions and 2 deletions

View File

@ -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)

View File

@ -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) #

View File

View 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)