diff options
Diffstat (limited to 'tests')
19 files changed, 526 insertions, 88 deletions
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index c94e6d3e95..ba59f9ae36 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -68,7 +68,7 @@ class ImprovedArticleWithParentLink(models.Model): article = models.OneToOneField(Article, parent_link=True) class BetterWriter(Writer): - pass + score = models.IntegerField() class WriterProfile(models.Model): writer = models.OneToOneField(Writer, primary_key=True) @@ -555,6 +555,8 @@ inserted as 'initial' data in each Field. <option value="3">Third test</option> </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> >>> 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() @@ -967,10 +969,20 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices >>> ImprovedArticleWithParentLinkForm.base_fields.keys() [] ->>> bw = BetterWriter(name=u'Joe Better') +>>> bw = BetterWriter(name=u'Joe Better', score=10) >>> bw.save() >>> sorted(model_to_dict(bw).keys()) -['id', 'name', 'writer_ptr'] +['id', 'name', 'score', 'writer_ptr'] + +>>> class BetterWriterForm(ModelForm): +... class Meta: +... model = BetterWriter +>>> form = BetterWriterForm({'name': 'Some Name', 'score': 12}) +>>> form.is_valid() +True +>>> bw2 = form.save() +>>> bw2.delete() + >>> class WriterProfileForm(ModelForm): ... class Meta: @@ -1102,16 +1114,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 -<FieldFile: None> - >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')}, instance=instance) >>> f.is_valid() True @@ -1358,27 +1360,35 @@ __test__['API_TESTS'] += """ ... class Meta: ... model = CommaSeparatedInteger ->>> f = CommaSeparatedIntegerForm().fields['field'] ->>> f.clean('1,2,3') -u'1,2,3' ->>> f.clean('1a,2') -Traceback (most recent call last): -... -ValidationError: [u'Enter only digits separated by commas.'] ->>> f.clean(',,,,') -u',,,,' ->>> f.clean('1.2') -Traceback (most recent call last): -... -ValidationError: [u'Enter only digits separated by commas.'] ->>> f.clean('1,a,2') -Traceback (most recent call last): -... -ValidationError: [u'Enter only digits separated by commas.'] ->>> f.clean('1,,2') -u'1,,2' ->>> f.clean('1') -u'1' +>>> f = CommaSeparatedIntegerForm({'field': '1,2,3'}) +>>> f.is_valid() +True +>>> f.cleaned_data +{'field': u'1,2,3'} +>>> f = CommaSeparatedIntegerForm({'field': '1a,2'}) +>>> f.errors +{'field': [u'Enter only digits separated by commas.']} +>>> f = CommaSeparatedIntegerForm({'field': ',,,,'}) +>>> f.is_valid() +True +>>> f.cleaned_data +{'field': u',,,,'} +>>> f = CommaSeparatedIntegerForm({'field': '1.2'}) +>>> f.errors +{'field': [u'Enter only digits separated by commas.']} +>>> f = CommaSeparatedIntegerForm({'field': '1,a,2'}) +>>> f.errors +{'field': [u'Enter only digits separated by commas.']} +>>> f = CommaSeparatedIntegerForm({'field': '1,,2'}) +>>> f.is_valid() +True +>>> f.cleaned_data +{'field': u'1,,2'} +>>> f = CommaSeparatedIntegerForm({'field': '1'}) +>>> f.is_valid() +True +>>> f.cleaned_data +{'field': u'1'} # unique/unique_together validation @@ -1415,13 +1425,16 @@ 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 +Traceback (most recent call last): + ... +UnresolvableValidationError: {'quantity': [u'This field cannot be null.']} # 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 1644ddf37b..5eab202962 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] @@ -1035,19 +1031,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/__init__.py b/tests/modeltests/validation/__init__.py new file mode 100644 index 0000000000..d0a7d19d49 --- /dev/null +++ b/tests/modeltests/validation/__init__.py @@ -0,0 +1,21 @@ +import unittest + +from django.core.exceptions import ValidationError + +class ValidationTestCase(unittest.TestCase): + def assertFailsValidation(self, clean, failed_fields): + self.assertRaises(ValidationError, clean) + try: + 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 new file mode 100644 index 0000000000..f1b0c5188c --- /dev/null +++ b/tests/modeltests/validation/models.py @@ -0,0 +1,53 @@ +from datetime import datetime +from django.core.exceptions import ValidationError +from django.db import models +from django.test import TestCase + + +def validate_answer_to_universe(value): + if value != 42: + raise ValidationError('This is not the answer to life, universe and everything!', code='not42') + +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) + url = models.URLField(blank=True) + f_with_custom_validator = models.IntegerField(blank=True, null=True, validators=[validate_answer_to_universe]) + + def validate(self): + super(ModelToValidate, self).validate() + if self.number == 11: + raise ValidationError('Invalid number supplied!') + +class UniqueFieldsModel(models.Model): + unique_charfield = models.CharField(max_length=100, unique=True) + unique_integerfield = models.IntegerField(unique=True) + non_unique_field = models.IntegerField() + +class CustomPKModel(models.Model): + my_pk_field = models.CharField(max_length=100, primary_key=True) + +class UniqueTogetherModel(models.Model): + cfield = models.CharField(max_length=100) + ifield = models.IntegerField() + efield = models.EmailField() + + class Meta: + unique_together = (('ifield', 'cfield',),('ifield', 'efield'), ) + +class UniqueForDateModel(models.Model): + start_date = models.DateField() + end_date = models.DateTimeField() + count = models.IntegerField(unique_for_date="start_date", unique_for_year="end_date") + order = models.IntegerField(unique_for_month="end_date") + name = models.CharField(max_length=100) + +class CustomMessagesModel(models.Model): + other = models.IntegerField(blank=True, null=True) + number = models.IntegerField( + error_messages={'null': 'NULL', 'not42': 'AAARGH', 'not_equal': '%s != me'}, + validators=[validate_answer_to_universe] + ) diff --git a/tests/modeltests/validation/test_custom_messages.py b/tests/modeltests/validation/test_custom_messages.py new file mode 100644 index 0000000000..9a958a0a3f --- /dev/null +++ b/tests/modeltests/validation/test_custom_messages.py @@ -0,0 +1,13 @@ +from modeltests.validation import ValidationTestCase +from models import CustomMessagesModel + + +class CustomMessagesTest(ValidationTestCase): + def test_custom_simple_validator_message(self): + cmm = CustomMessagesModel(number=12) + self.assertFieldFailsValidationWithMessage(cmm.full_validate, 'number', ['AAARGH']) + + def test_custom_null_message(self): + cmm = CustomMessagesModel() + self.assertFieldFailsValidationWithMessage(cmm.full_validate, 'number', ['NULL']) + diff --git a/tests/modeltests/validation/test_unique.py b/tests/modeltests/validation/test_unique.py new file mode 100644 index 0000000000..cbb56aa8f5 --- /dev/null +++ b/tests/modeltests/validation/test_unique.py @@ -0,0 +1,58 @@ +import unittest +from django.conf import settings +from django.db import connection +from models import CustomPKModel, UniqueTogetherModel, UniqueFieldsModel, UniqueForDateModel, ModelToValidate + + +class GetUniqueCheckTests(unittest.TestCase): + def test_unique_fields_get_collected(self): + m = UniqueFieldsModel() + self.assertEqual( + ([('id',), ('unique_charfield',), ('unique_integerfield',)], []), + m._get_unique_checks() + ) + + def test_unique_together_gets_picked_up(self): + m = UniqueTogetherModel() + self.assertEqual( + ([('ifield', 'cfield',),('ifield', 'efield'), ('id',), ], []), + m._get_unique_checks() + ) + + def test_primary_key_is_considered_unique(self): + m = CustomPKModel() + self.assertEqual(([('my_pk_field',)], []), m._get_unique_checks()) + + def test_unique_for_date_gets_picked_up(self): + m = UniqueForDateModel() + self.assertEqual(( + [('id',)], + [('date', 'count', 'start_date'), ('year', 'count', 'end_date'), ('month', 'order', 'end_date')] + ), m._get_unique_checks() + ) + +class PerformUniqueChecksTest(unittest.TestCase): + def setUp(self): + # Set debug to True to gain access to connection.queries. + self._old_debug, settings.DEBUG = settings.DEBUG, True + super(PerformUniqueChecksTest, self).setUp() + + def tearDown(self): + # Restore old debug value. + settings.DEBUG = self._old_debug + super(PerformUniqueChecksTest, self).tearDown() + + def test_primary_key_unique_check_performed_when_adding(self): + """Regression test for #12132""" + l = len(connection.queries) + mtv = ModelToValidate(number=10, name='Some Name') + setattr(mtv, '_adding', True) + mtv.full_validate() + self.assertEqual(l+1, len(connection.queries)) + + def test_primary_key_unique_check_not_performed_when_not_adding(self): + """Regression test for #12132""" + l = len(connection.queries) + mtv = ModelToValidate(number=10, name='Some Name') + mtv.full_validate() + self.assertEqual(l, len(connection.queries)) diff --git a/tests/modeltests/validation/tests.py b/tests/modeltests/validation/tests.py new file mode 100644 index 0000000000..c00070b2ab --- /dev/null +++ b/tests/modeltests/validation/tests.py @@ -0,0 +1,58 @@ +from django.core.exceptions import ValidationError, NON_FIELD_ERRORS +from django.db import models + +from modeltests.validation import ValidationTestCase +from models import * + +from validators import TestModelsWithValidators +from test_unique import GetUniqueCheckTests, PerformUniqueChecksTest +from test_custom_messages import CustomMessagesTest + + +class BaseModelValidationTests(ValidationTestCase): + + def test_missing_required_field_raises_error(self): + mtv = ModelToValidate(f_with_custom_validator=42) + self.assertFailsValidation(mtv.full_validate, ['name', 'number']) + + def test_with_correct_value_model_validates(self): + mtv = ModelToValidate(number=10, name='Some Name') + self.assertEqual(None, mtv.full_validate()) + + def test_custom_validate_method_is_called(self): + mtv = ModelToValidate(number=11) + self.assertFailsValidation(mtv.full_validate, [NON_FIELD_ERRORS, 'name']) + + def test_wrong_FK_value_raises_error(self): + mtv=ModelToValidate(number=10, name='Some Name', parent_id=3) + self.assertFailsValidation(mtv.full_validate, ['parent']) + + def test_correct_FK_value_validates(self): + parent = ModelToValidate.objects.create(number=10, name='Some Name') + mtv=ModelToValidate(number=10, name='Some Name', parent_id=parent.pk) + self.assertEqual(None, mtv.full_validate()) + + def test_wrong_email_value_raises_error(self): + mtv = ModelToValidate(number=10, name='Some Name', email='not-an-email') + self.assertFailsValidation(mtv.full_validate, ['email']) + + def test_correct_email_value_passes(self): + mtv = ModelToValidate(number=10, name='Some Name', email='valid@email.com') + self.assertEqual(None, mtv.full_validate()) + + def test_wrong_url_value_raises_error(self): + mtv = ModelToValidate(number=10, name='Some Name', url='not a url') + self.assertFieldFailsValidationWithMessage(mtv.full_validate, 'url', [u'Enter a valid value.']) + + def test_correct_url_but_nonexisting_gives_404(self): + mtv = ModelToValidate(number=10, name='Some Name', url='http://google.com/we-love-microsoft.html') + self.assertFieldFailsValidationWithMessage(mtv.full_validate, 'url', [u'This URL appears to be a broken link.']) + + def test_correct_url_value_passes(self): + mtv = ModelToValidate(number=10, name='Some Name', url='http://www.djangoproject.com/') + self.assertEqual(None, mtv.full_validate()) # This will fail if there's no Internet connection + + def test_text_greater_that_charfields_max_length_eaises_erros(self): + mtv = ModelToValidate(number=10, name='Some Name'*100) + self.assertFailsValidation(mtv.full_validate, ['name',]) + diff --git a/tests/modeltests/validation/validators.py b/tests/modeltests/validation/validators.py new file mode 100644 index 0000000000..dc4cd4eba1 --- /dev/null +++ b/tests/modeltests/validation/validators.py @@ -0,0 +1,18 @@ +from unittest import TestCase +from modeltests.validation import ValidationTestCase +from models import * + + +class TestModelsWithValidators(ValidationTestCase): + def test_custom_validator_passes_for_correct_value(self): + mtv = ModelToValidate(number=10, name='Some Name', f_with_custom_validator=42) + self.assertEqual(None, mtv.full_validate()) + + 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.full_validate, ['f_with_custom_validator']) + self.assertFieldFailsValidationWithMessage( + mtv.full_validate, + 'f_with_custom_validator', + [u'This is not the answer to life, universe and everything!'] + ) diff --git a/tests/modeltests/validators/tests.py b/tests/modeltests/validators/tests.py new file mode 100644 index 0000000000..f36d8c4713 --- /dev/null +++ b/tests/modeltests/validators/tests.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +import re +import types +from unittest import TestCase +from datetime import datetime, timedelta +from django.core.exceptions import ValidationError +from django.core.validators import * + +NOW = datetime.now() + +TEST_DATA = ( + # (validator, value, expected), + (validate_integer, '42', None), + (validate_integer, '-42', None), + (validate_integer, -42, None), + (validate_integer, -42.5, None), + + (validate_integer, None, ValidationError), + (validate_integer, 'a', ValidationError), + + (validate_email, 'email@here.com', None), + (validate_email, 'weirder-email@here.and.there.com', None), + + (validate_email, None, ValidationError), + (validate_email, '', ValidationError), + (validate_email, 'abc', ValidationError), + (validate_email, 'a @x.cz', ValidationError), + (validate_email, 'something@@somewhere.com', ValidationError), + + (validate_slug, 'slug-ok', None), + (validate_slug, 'longer-slug-still-ok', None), + (validate_slug, '--------', None), + (validate_slug, 'nohyphensoranything', None), + + (validate_slug, '', ValidationError), + (validate_slug, ' text ', ValidationError), + (validate_slug, ' ', ValidationError), + (validate_slug, 'some@mail.com', ValidationError), + (validate_slug, '你好', ValidationError), + (validate_slug, '\n', ValidationError), + + (validate_ipv4_address, '1.1.1.1', None), + (validate_ipv4_address, '255.0.0.0', None), + (validate_ipv4_address, '0.0.0.0', None), + + (validate_ipv4_address, '256.1.1.1', ValidationError), + (validate_ipv4_address, '25.1.1.', ValidationError), + (validate_ipv4_address, '25,1,1,1', ValidationError), + (validate_ipv4_address, '25.1 .1.1', ValidationError), + + (validate_comma_separated_integer_list, '1', None), + (validate_comma_separated_integer_list, '1,2,3', None), + (validate_comma_separated_integer_list, '1,2,3,', None), + + (validate_comma_separated_integer_list, '', ValidationError), + (validate_comma_separated_integer_list, 'a,b,c', ValidationError), + (validate_comma_separated_integer_list, '1, 2, 3', ValidationError), + + (MaxValueValidator(10), 10, None), + (MaxValueValidator(10), -10, None), + (MaxValueValidator(10), 0, None), + (MaxValueValidator(NOW), NOW, None), + (MaxValueValidator(NOW), NOW - timedelta(days=1), None), + + (MaxValueValidator(0), 1, ValidationError), + (MaxValueValidator(NOW), NOW + timedelta(days=1), ValidationError), + + (MinValueValidator(-10), -10, None), + (MinValueValidator(-10), 10, None), + (MinValueValidator(-10), 0, None), + (MinValueValidator(NOW), NOW, None), + (MinValueValidator(NOW), NOW + timedelta(days=1), None), + + (MinValueValidator(0), -1, ValidationError), + (MinValueValidator(NOW), NOW - timedelta(days=1), ValidationError), + + (MaxLengthValidator(10), '', None), + (MaxLengthValidator(10), 10*'x', None), + + (MaxLengthValidator(10), 15*'x', ValidationError), + + (MinLengthValidator(10), 15*'x', None), + (MinLengthValidator(10), 10*'x', None), + + (MinLengthValidator(10), '', ValidationError), + + (URLValidator(), 'http://www.djangoproject.com/', None), + (URLValidator(), 'http://localhost/', None), + (URLValidator(), 'http://example.com/', None), + (URLValidator(), 'http://www.example.com/', None), + (URLValidator(), 'http://www.example.com:8000/test', None), + (URLValidator(), 'http://valid-with-hyphens.com/', None), + (URLValidator(), 'http://subdomain.domain.com/', None), + (URLValidator(), 'http://200.8.9.10/', None), + (URLValidator(), 'http://200.8.9.10:8000/test', None), + (URLValidator(), 'http://valid-----hyphens.com/', None), + (URLValidator(), 'http://example.com?something=value', None), + (URLValidator(), 'http://example.com/index.php?something=value&another=value2', None), + + (URLValidator(), 'foo', ValidationError), + (URLValidator(), 'http://', ValidationError), + (URLValidator(), 'http://example', ValidationError), + (URLValidator(), 'http://example.', ValidationError), + (URLValidator(), 'http://.com', ValidationError), + (URLValidator(), 'http://invalid-.com', ValidationError), + (URLValidator(), 'http://-invalid.com', ValidationError), + (URLValidator(), 'http://inv-.alid-.com', ValidationError), + (URLValidator(), 'http://inv-.-alid.com', ValidationError), + + (BaseValidator(True), True, None), + (BaseValidator(True), False, ValidationError), + + (RegexValidator('.*'), '', None), + (RegexValidator(re.compile('.*')), '', None), + (RegexValidator('.*'), 'xxxxx', None), + + (RegexValidator('x'), 'y', ValidationError), + (RegexValidator(re.compile('x')), 'y', ValidationError), +) + +def create_simple_test_method(validator, expected, value, num): + if isinstance(expected, type) and issubclass(expected, Exception): + test_mask = 'test_%s_raises_error_%d' + def test_func(self): + self.assertRaises(expected, validator, value) + else: + test_mask = 'test_%s_%d' + def test_func(self): + self.assertEqual(expected, validator(value)) + if isinstance(validator, types.FunctionType): + val_name = validator.__name__ + else: + val_name = validator.__class__.__name__ + test_name = test_mask % (val_name, num) + return test_name, test_func + +# Dynamically assemble a test class with the contents of TEST_DATA + +class TestSimpleValidators(TestCase): + pass + +test_counter = 0 +for validator, value, expected in TEST_DATA: + name, method = create_simple_test_method(validator, expected, value, test_counter) + setattr(TestSimpleValidators, name, method) + test_counter += 1 diff --git a/tests/regressiontests/forms/error_messages.py b/tests/regressiontests/forms/error_messages.py index b7224dbde0..038fa39f6b 100644 --- a/tests/regressiontests/forms/error_messages.py +++ b/tests/regressiontests/forms/error_messages.py @@ -6,8 +6,8 @@ tests = r""" # CharField ################################################################### >>> e = {'required': 'REQUIRED'} ->>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s' ->>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s' +>>> e['min_length'] = 'LENGTH %(show_value)s, MIN LENGTH %(limit_value)s' +>>> e['max_length'] = 'LENGTH %(show_value)s, MAX LENGTH %(limit_value)s' >>> f = CharField(min_length=5, max_length=10, error_messages=e) >>> f.clean('') Traceback (most recent call last): @@ -26,8 +26,8 @@ ValidationError: [u'LENGTH 11, MAX LENGTH 10'] >>> e = {'required': 'REQUIRED'} >>> e['invalid'] = 'INVALID' ->>> e['min_value'] = 'MIN VALUE IS %s' ->>> e['max_value'] = 'MAX VALUE IS %s' +>>> e['min_value'] = 'MIN VALUE IS %(limit_value)s' +>>> e['max_value'] = 'MAX VALUE IS %(limit_value)s' >>> f = IntegerField(min_value=5, max_value=10, error_messages=e) >>> f.clean('') Traceback (most recent call last): @@ -50,8 +50,8 @@ ValidationError: [u'MAX VALUE IS 10'] >>> e = {'required': 'REQUIRED'} >>> e['invalid'] = 'INVALID' ->>> e['min_value'] = 'MIN VALUE IS %s' ->>> e['max_value'] = 'MAX VALUE IS %s' +>>> e['min_value'] = 'MIN VALUE IS %(limit_value)s' +>>> e['max_value'] = 'MAX VALUE IS %(limit_value)s' >>> f = FloatField(min_value=5, max_value=10, error_messages=e) >>> f.clean('') Traceback (most recent call last): @@ -74,8 +74,8 @@ ValidationError: [u'MAX VALUE IS 10'] >>> e = {'required': 'REQUIRED'} >>> e['invalid'] = 'INVALID' ->>> e['min_value'] = 'MIN VALUE IS %s' ->>> e['max_value'] = 'MAX VALUE IS %s' +>>> e['min_value'] = 'MIN VALUE IS %(limit_value)s' +>>> e['max_value'] = 'MAX VALUE IS %(limit_value)s' >>> e['max_digits'] = 'MAX DIGITS IS %s' >>> e['max_decimal_places'] = 'MAX DP IS %s' >>> e['max_whole_digits'] = 'MAX DIGITS BEFORE DP IS %s' @@ -156,8 +156,8 @@ ValidationError: [u'INVALID'] >>> e = {'required': 'REQUIRED'} >>> e['invalid'] = 'INVALID' ->>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s' ->>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s' +>>> e['min_length'] = 'LENGTH %(show_value)s, MIN LENGTH %(limit_value)s' +>>> e['max_length'] = 'LENGTH %(show_value)s, MAX LENGTH %(limit_value)s' >>> f = RegexField(r'^\d+$', min_length=5, max_length=10, error_messages=e) >>> f.clean('') Traceback (most recent call last): @@ -180,8 +180,8 @@ ValidationError: [u'LENGTH 11, MAX LENGTH 10'] >>> e = {'required': 'REQUIRED'} >>> e['invalid'] = 'INVALID' ->>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s' ->>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s' +>>> e['min_length'] = 'LENGTH %(show_value)s, MIN LENGTH %(limit_value)s' +>>> e['max_length'] = 'LENGTH %(show_value)s, MAX LENGTH %(limit_value)s' >>> f = EmailField(min_length=8, max_length=10, error_messages=e) >>> f.clean('') Traceback (most recent call last): diff --git a/tests/regressiontests/forms/fields.py b/tests/regressiontests/forms/fields.py index c9736d38e1..87330904df 100644 --- a/tests/regressiontests/forms/fields.py +++ b/tests/regressiontests/forms/fields.py @@ -386,7 +386,7 @@ class FieldsTests(TestCase): def test_regexfield_31(self): f = RegexField('^\d+$', min_length=5, max_length=10) self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at least 5 characters (it has 3).']", f.clean, '123') - self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at least 5 characters (it has 3).']", f.clean, 'abc') + self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at least 5 characters (it has 3).', u'Enter a valid value.']", f.clean, 'abc') self.assertEqual(u'12345', f.clean('12345')) self.assertEqual(u'1234567890', f.clean('1234567890')) self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at most 10 characters (it has 11).']", f.clean, '12345678901') @@ -548,6 +548,10 @@ class FieldsTests(TestCase): self.assertEqual(u'http://example.com/', f.clean('http://example.com')) self.assertEqual(u'http://example.com/test', f.clean('http://example.com/test')) + def test_urlfield_ticket11826(self): + f = URLField() + self.assertEqual(u'http://example.com/?some_param=some_value', f.clean('http://example.com?some_param=some_value')) + # BooleanField ################################################################ def test_booleanfield_44(self): diff --git a/tests/regressiontests/forms/localflavor/ar.py b/tests/regressiontests/forms/localflavor/ar.py index e1c827c4a0..c7967c84da 100644 --- a/tests/regressiontests/forms/localflavor/ar.py +++ b/tests/regressiontests/forms/localflavor/ar.py @@ -28,7 +28,7 @@ u'C1064AAB' >>> f.clean('C1064AABB') Traceback (most recent call last): ... -ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] +ValidationError: [u'Ensure this value has at most 8 characters (it has 9).', u'Enter a postal code in the format NNNN or ANNNNAAA.'] >>> f.clean('C1064AA') Traceback (most recent call last): ... @@ -44,7 +44,7 @@ ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] >>> f.clean('500') Traceback (most recent call last): ... -ValidationError: [u'Ensure this value has at least 4 characters (it has 3).'] +ValidationError: [u'Ensure this value has at least 4 characters (it has 3).', u'Enter a postal code in the format NNNN or ANNNNAAA.'] >>> f.clean('5PPP') Traceback (most recent call last): ... @@ -78,7 +78,7 @@ u'C1064AAB' >>> f.clean('C1064AABB') Traceback (most recent call last): ... -ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] +ValidationError: [u'Ensure this value has at most 8 characters (it has 9).', u'Enter a postal code in the format NNNN or ANNNNAAA.'] >>> f.clean('C1064AA') Traceback (most recent call last): ... @@ -94,7 +94,7 @@ ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] >>> f.clean('500') Traceback (most recent call last): ... -ValidationError: [u'Ensure this value has at least 4 characters (it has 3).'] +ValidationError: [u'Ensure this value has at least 4 characters (it has 3).', u'Enter a postal code in the format NNNN or ANNNNAAA.'] >>> f.clean('5PPP') Traceback (most recent call last): ... diff --git a/tests/regressiontests/forms/localflavor/is_.py b/tests/regressiontests/forms/localflavor/is_.py index 6851441a79..e71c2dd8de 100644 --- a/tests/regressiontests/forms/localflavor/is_.py +++ b/tests/regressiontests/forms/localflavor/is_.py @@ -15,11 +15,11 @@ u'230880-3449' >>> f.clean('230880343') Traceback (most recent call last): ... -ValidationError: [u'Ensure this value has at least 10 characters (it has 9).'] +ValidationError: [u'Ensure this value has at least 10 characters (it has 9).', u'Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.'] >>> f.clean('230880343234') Traceback (most recent call last): ... -ValidationError: [u'Ensure this value has at most 11 characters (it has 12).'] +ValidationError: [u'Ensure this value has at most 11 characters (it has 12).', u'Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.'] >>> f.clean('abcdefghijk') Traceback (most recent call last): ... @@ -61,18 +61,18 @@ ValidationError: [u'Enter a valid value.'] >>> f.clean('123456') Traceback (most recent call last): ... -ValidationError: [u'Ensure this value has at least 7 characters (it has 6).'] +ValidationError: [u'Ensure this value has at least 7 characters (it has 6).', u'Enter a valid value.'] >>> f.clean('123456555') Traceback (most recent call last): ... -ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] +ValidationError: [u'Ensure this value has at most 8 characters (it has 9).', u'Enter a valid value.'] >>> f.clean('abcdefg') Traceback (most recent call last): ValidationError: [u'Enter a valid value.'] >>> f.clean(' 1234567 ') Traceback (most recent call last): ... -ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] +ValidationError: [u'Ensure this value has at most 8 characters (it has 9).', u'Enter a valid value.'] >>> f.clean(' 12367 ') Traceback (most recent call last): ... diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index 89140f04b1..db70500909 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -38,6 +38,7 @@ from formsets import tests as formset_tests from media import media_tests from fields import FieldsTests +from validators import TestFieldWithValidators __test__ = { 'extra_tests': extra_tests, diff --git a/tests/regressiontests/forms/util.py b/tests/regressiontests/forms/util.py index 845ddeaadb..f365c8c1ae 100644 --- a/tests/regressiontests/forms/util.py +++ b/tests/regressiontests/forms/util.py @@ -5,6 +5,7 @@ Tests for forms/util.py module. tests = r""" >>> from django.forms.util import * +>>> from django.core.exceptions import ValidationError >>> from django.utils.translation import ugettext_lazy ########### @@ -24,36 +25,36 @@ u'' ################### # Can take a string. ->>> print ValidationError("There was an error.").messages +>>> print ErrorList(ValidationError("There was an error.").messages) <ul class="errorlist"><li>There was an error.</li></ul> # Can take a unicode string. ->>> print ValidationError(u"Not \u03C0.").messages +>>> print ErrorList(ValidationError(u"Not \u03C0.").messages) <ul class="errorlist"><li>Not π.</li></ul> # Can take a lazy string. ->>> print ValidationError(ugettext_lazy("Error.")).messages +>>> print ErrorList(ValidationError(ugettext_lazy("Error.")).messages) <ul class="errorlist"><li>Error.</li></ul> # Can take a list. ->>> print ValidationError(["Error one.", "Error two."]).messages +>>> print ErrorList(ValidationError(["Error one.", "Error two."]).messages) <ul class="errorlist"><li>Error one.</li><li>Error two.</li></ul> # Can take a mixture in a list. ->>> print ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages +>>> print ErrorList(ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages) <ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul> >>> class VeryBadError: ... def __unicode__(self): return u"A very bad error." # Can take a non-string. ->>> print ValidationError(VeryBadError()).messages +>>> print ErrorList(ValidationError(VeryBadError()).messages) <ul class="errorlist"><li>A very bad error.</li></ul> # Escapes non-safe input but not input marked safe. >>> example = 'Example of link: <a href="http://www.example.com/">example</a>' ->>> print ValidationError(example).messages +>>> print ErrorList([example]) <ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></li></ul> ->>> print ValidationError(mark_safe(example)).messages +>>> print ErrorList([mark_safe(example)]) <ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></li></ul> """ diff --git a/tests/regressiontests/forms/validators.py b/tests/regressiontests/forms/validators.py new file mode 100644 index 0000000000..ed8e8fbd9a --- /dev/null +++ b/tests/regressiontests/forms/validators.py @@ -0,0 +1,17 @@ +from unittest import TestCase + +from django import forms +from django.core import validators +from django.core.exceptions import ValidationError + + +class TestFieldWithValidators(TestCase): + def test_all_errors_get_reported(self): + field = forms.CharField( + validators=[validators.validate_integer, validators.validate_email] + ) + self.assertRaises(ValidationError, field.clean, 'not int nor mail') + try: + field.clean('not int nor mail') + except ValidationError, e: + self.assertEqual(2, len(e.messages)) diff --git a/tests/regressiontests/inline_formsets/tests.py b/tests/regressiontests/inline_formsets/tests.py index aef6b3f10a..be313f3bf6 100644 --- a/tests/regressiontests/inline_formsets/tests.py +++ b/tests/regressiontests/inline_formsets/tests.py @@ -81,7 +81,7 @@ class DeletionTests(TestCase): regression for #10750 """ # exclude some required field from the forms - ChildFormSet = inlineformset_factory(School, Child, exclude=['father', 'mother']) + ChildFormSet = inlineformset_factory(School, Child) school = School.objects.create(name=u'test') mother = Parent.objects.create(name=u'mother') father = Parent.objects.create(name=u'father') @@ -89,13 +89,13 @@ class DeletionTests(TestCase): 'child_set-TOTAL_FORMS': u'1', 'child_set-INITIAL_FORMS': u'0', 'child_set-0-name': u'child', + 'child_set-0-mother': unicode(mother.pk), + 'child_set-0-father': unicode(father.pk), } formset = ChildFormSet(data, instance=school) self.assertEqual(formset.is_valid(), True) objects = formset.save(commit=False) - for obj in objects: - obj.mother = mother - obj.father = father - obj.save() + self.assertEqual(school.child_set.count(), 0) + objects[0].save() self.assertEqual(school.child_set.count(), 1) diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py index f31193e269..ea7b49ab7e 100644 --- a/tests/regressiontests/model_fields/tests.py +++ b/tests/regressiontests/model_fields/tests.py @@ -147,6 +147,58 @@ 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_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): + 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(max_length=1, 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_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): + 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) + + class BigIntegerFieldTests(django.test.TestCase): def test_limits(self): # Ensure that values that are right at the limits can be saved diff --git a/tests/regressiontests/views/views.py b/tests/regressiontests/views/views.py index 3447cbfdfd..20ee56f985 100644 --- a/tests/regressiontests/views/views.py +++ b/tests/regressiontests/views/views.py @@ -24,7 +24,7 @@ def custom_create(request): model = Article def save(self, *args, **kwargs): - self.cleaned_data['slug'] = 'some-other-slug' + self.instance.slug = 'some-other-slug' return super(SlugChangingArticleForm, self).save(*args, **kwargs) return create_object(request, |
