summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHonza Král <honza.kral@gmail.com>2009-06-01 15:38:34 +0000
committerHonza Král <honza.kral@gmail.com>2009-06-01 15:38:34 +0000
commitb59bfbd128a882778f4d96ac9a29880791f728b9 (patch)
tree395cc96bf217dc1a9263cee7f9d7baadc4096cf0
parent55e23d5efaa11b7385c9c0d117378b07cdeb7341 (diff)
[soc2009/model-validation] Implemented some basic validation on model Fields including basic tests.
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@10868 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/db/models/fields/__init__.py37
-rw-r--r--django/db/models/fields/related.py13
-rw-r--r--tests/regressiontests/model_fields/tests.py42
3 files changed, 85 insertions, 7 deletions
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index a3007a7f66..327a236123 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -117,6 +117,32 @@ class Field(object):
Returns the converted value. Subclasses should override this.
"""
return value
+
+ def validate(self, value, model_instance):
+ """
+ Validates value and throws ValidationError. Subclasses should override
+ this to provide validation logic.
+ """
+ if not self.editable:
+ # skip validation for non-editable fields
+ return
+ if self._choices and value:
+ if not value in dict(self.choices):
+ raise exceptions.ValidationError(_('Value %r is not a valid choice.') % value)
+
+ if value is None and not self.null:
+ raise exceptions.ValidationError(
+ ugettext_lazy("This field cannot be null."))
+
+ def clean(self, value, model_instance):
+ """
+ Convert the value's type and wun validation. Validation errors from to_python
+ and validate are propagated. The correct value is returned if no error is
+ raised.
+ """
+ value = self.to_python(value)
+ self.validate(value, model_instance)
+ return value
def db_type(self):
"""
@@ -354,6 +380,9 @@ class AutoField(Field):
except (TypeError, ValueError):
raise exceptions.ValidationError(
_("This value must be an integer."))
+
+ def validate(self, value, model_instance):
+ pass
def get_db_prep_value(self, value):
if value is None:
@@ -417,14 +446,8 @@ class CharField(Field):
return "CharField"
def to_python(self, value):
- if isinstance(value, basestring):
+ if isinstance(value, basestring) or value is None:
return value
- if value is None:
- if self.null:
- return value
- else:
- raise exceptions.ValidationError(
- ugettext_lazy("This field cannot be null."))
return smart_unicode(value)
def formfield(self, **kwargs):
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 419695b74b..d4b921bc1d 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -678,6 +678,11 @@ class ForeignKey(RelatedField, Field):
self.db_index = True
+ def validate(self, value, model_instance):
+ if self.rel.parent_link:
+ return
+ super(ForeignKey, self).validate(value, model_instance)
+
def get_attname(self):
return '%s_id' % self.name
@@ -766,6 +771,14 @@ class OneToOneField(ForeignKey):
return None
return super(OneToOneField, self).formfield(**kwargs)
+ def save_form_data(self, instance, data):
+ # FIXME: is this a hack, or what? it works, but I don't really know why
+ if isinstance(data, self.rel.to):
+ setattr(instance, self.name, data)
+ else:
+ setattr(instance, self.attname, data)
+
+
class ManyToManyField(RelatedField, Field):
def __init__(self, to, **kwargs):
try:
diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py
index 0815d9ef3c..7335723978 100644
--- a/tests/regressiontests/model_fields/tests.py
+++ b/tests/regressiontests/model_fields/tests.py
@@ -132,3 +132,45 @@ class SlugFieldTests(django.test.TestCase):
bs = BigS.objects.get(pk=bs.pk)
self.assertEqual(bs.s, 'slug'*50)
+class ValidationTest(django.test.TestCase):
+ def test_charfield_cleans_empty_string(self):
+ f = models.CharField()
+ self.assertEqual('', f.clean('', None))
+
+ def test_integerfield_cleans_valid_string(self):
+ f = models.IntegerField()
+ self.assertEqual(2, f.clean('2', None))
+
+ def test_integerfield_raises_error_on_invalid_intput(self):
+ f = models.IntegerField()
+ self.assertRaises(ValidationError, f.clean, "a", None)
+
+ def test_charfield_with_choices_cleans_valid_choice(self):
+ f = models.CharField(choices=[('a','A'), ('b','B')])
+ self.assertEqual('a', f.clean('a', None))
+
+ def test_charfield_with_choices_raises_error_on_invalid_choice(self):
+ f = models.CharField(choices=[('a','A'), ('b','B')])
+ self.assertRaises(ValidationError, f.clean, "not a", None)
+
+ def test_nullable_integerfield_cleans_none(self):
+ f = models.IntegerField(null=True)
+ self.assertEqual(None, f.clean(None, None))
+
+ def test_integerfield_raises_error_on_empty_input(self):
+ f = models.IntegerField(null=False)
+ self.assertRaises(ValidationError, f.clean, None, None)
+ self.assertRaises(ValidationError, f.clean, '', None)
+
+ def test_charfield_raises_error_on_empty_input(self):
+ f = models.CharField(null=False)
+ self.assertRaises(ValidationError, f.clean, None, None)
+
+ def test_datefield_cleans_date(self):
+ f = models.DateField()
+ self.assertEqual(datetime.date(2008, 10, 10), f.clean('2008-10-10', None))
+
+ def test_boolean_field_doesnt_accept_empty_input(self):
+ f = models.BooleanField()
+ self.assertRaises(ValidationError, f.clean, None, None)
+