diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 327a236123..bc3b27fb6a 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -134,6 +134,12 @@ class Field(object):
raise exceptions.ValidationError(
ugettext_lazy("This field cannot be null."))
+ # cannot do if not value because of 0 passed to integer fields
+ if not self.blank and value in ( None, '' ):
+ raise exceptions.ValidationError(
+ ugettext_lazy("This field cannot be blank."))
+
+
def clean(self, value, model_instance):
"""
Convert the value's type and wun validation. Validation errors from to_python
diff --git a/django/forms/models.py b/django/forms/models.py
index b0eb2f5bec..40cde06bc1 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -242,6 +242,22 @@ class BaseModelForm(BaseForm):
opts = self._meta
self.instance = make_instance(self, self.instance, opts.fields, opts.exclude)
self.validate_unique()
+ try:
+ # FIMXE: what to do about duplicate errors? (is required etc.)
+ self.instance.clean()
+ except ValidationError, e:
+ for k, v in e.message_dict.items():
+ if k != NON_FIELD_ERRORS:
+ self._errors.setdefault(k, []).extend(v)
+
+ # Remove the data from the cleaned_data dict since it was invalid
+ if k in self.cleaned_data:
+ del self.cleaned_data[k]
+
+ # what about fields that don't validate but aren't present on the form?
+ if NON_FIELD_ERRORS in e.message_dict:
+ raise ValidationError(e.message_dict[NON_FIELD_ERRORS])
+
return self.cleaned_data
def validate_unique(self):
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
index 95fda273f0..a8a414143e 100644
--- a/tests/modeltests/model_forms/models.py
+++ b/tests/modeltests/model_forms/models.py
@@ -449,9 +449,9 @@ u'third-test'
If you call save() with invalid data, you'll get a ValueError.
>>> f = CategoryForm({'name': '', 'slug': 'not a slug!', 'url': 'foo'})
>>> f.errors['name']
-[u'This field is required.']
+[u'This field is required.', u'This field cannot be blank.']
>>> f.errors['slug']
-[u"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."]
+[u"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", u'This field cannot be blank.']
>>> f.cleaned_data
Traceback (most recent call last):
...
@@ -555,6 +555,8 @@ inserted as 'initial' data in each Field.
Hold down "Control", or "Command" on a Mac, to select more than one.
>>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'}, instance=art)
+>>> f.errors
+{}
>>> f.is_valid()
True
>>> test_art = f.save()
@@ -1102,16 +1104,6 @@ True
>>> instance.delete()
-# Test the non-required FileField
-
->>> f = TextFileForm(data={'description': u'Assistance'})
->>> f.fields['file'].required = False
->>> f.is_valid()
-True
->>> instance = f.save()
->>> instance.file
-
-
>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')}, instance=instance)
>>> f.is_valid()
True
@@ -1390,13 +1382,14 @@ False
>>> form._errors
{'__all__': [u'Price with this Price and Quantity already exists.']}
+# this form is never valid because quantity is blank=False
>>> class PriceForm(ModelForm):
... class Meta:
... model = Price
... exclude = ('quantity',)
>>> form = PriceForm({'price': '6.00'})
>>> form.is_valid()
-True
+False
# Unique & unique together with null values
>>> class BookForm(ModelForm):
diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py
index cda3aa9d8e..692c52c982 100644
--- a/tests/modeltests/model_formsets/models.py
+++ b/tests/modeltests/model_formsets/models.py
@@ -543,10 +543,6 @@ This is used in the admin for save_as functionality.
... 'book_set-2-title': '',
... }
->>> formset = AuthorBooksFormSet(data, instance=Author(), save_as_new=True)
->>> formset.is_valid()
-True
-
>>> new_author = Author.objects.create(name='Charles Baudelaire')
>>> formset = AuthorBooksFormSet(data, instance=new_author, save_as_new=True)
>>> [book for book in formset.save() if book.author.pk == new_author.pk]
@@ -988,19 +984,6 @@ False
>>> formset._non_form_errors
[u'Please correct the duplicate data for price and quantity, which must be unique.']
-# only the price field is specified, this should skip any unique checks since the unique_together is not fulfilled.
-# this will fail with a KeyError if broken.
->>> FormSet = modelformset_factory(Price, fields=("price",), extra=2)
->>> data = {
-... 'form-TOTAL_FORMS': '2',
-... 'form-INITIAL_FORMS': '0',
-... 'form-0-price': '24',
-... 'form-1-price': '24',
-... }
->>> formset = FormSet(data)
->>> formset.is_valid()
-True
-
>>> FormSet = inlineformset_factory(Author, Book, extra=0)
>>> author = Author.objects.order_by('id')[0]
>>> book_ids = author.book_set.values_list('id', flat=True)
diff --git a/tests/modeltests/validation/models.py b/tests/modeltests/validation/models.py
index d536495da7..bce49741a1 100644
--- a/tests/modeltests/validation/models.py
+++ b/tests/modeltests/validation/models.py
@@ -18,11 +18,16 @@ class BaseModelValidationTests(TestCase):
def test_missing_required_field_raises_error(self):
mtv = ModelToValidate()
self.assertRaises(ValidationError, mtv.clean)
+ try:
+ mtv.clean()
+ except ValidationError, e:
+ self.assertEquals(['name', 'number'], sorted(e.message_dict.keys()))
def test_with_correct_value_model_validates(self):
- mtv = ModelToValidate(number=10)
+ mtv = ModelToValidate(number=10, name='Some Name')
self.assertEqual(None, mtv.clean())
def test_custom_validate_method_is_called(self):
mtv = ModelToValidate(number=11)
self.assertRaises(ValidationError, mtv.clean)
+
diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py
index 7335723978..4706c210a7 100644
--- a/tests/regressiontests/model_fields/tests.py
+++ b/tests/regressiontests/model_fields/tests.py
@@ -133,8 +133,12 @@ class SlugFieldTests(django.test.TestCase):
self.assertEqual(bs.s, 'slug'*50)
class ValidationTest(django.test.TestCase):
- def test_charfield_cleans_empty_string(self):
+ def test_charfield_raises_error_on_empty_string(self):
f = models.CharField()
+ self.assertRaises(ValidationError, f.clean, "", None)
+
+ def test_charfield_cleans_empty_string_when_blank_true(self):
+ f = models.CharField(blank=True)
self.assertEqual('', f.clean('', None))
def test_integerfield_cleans_valid_string(self):
@@ -153,8 +157,12 @@ class ValidationTest(django.test.TestCase):
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)
+ def test_nullable_integerfield_raises_error_with_blank_false(self):
+ f = models.IntegerField(null=True, blank=False)
+ self.assertRaises(ValidationError, f.clean, None, None)
+
+ def test_nullable_integerfield_cleans_none_on_null_and_blank_true(self):
+ f = models.IntegerField(null=True, blank=True)
self.assertEqual(None, f.clean(None, None))
def test_integerfield_raises_error_on_empty_input(self):