Ticket #12881: 12881.diff
File 12881.diff, 22.9 KB (added by , 15 years ago) |
---|
-
django/db/models/base.py
682 682 if exclude is None: 683 683 exclude = [] 684 684 unique_checks = [] 685 for check in self._meta.unique_together:686 for name in check:687 # If this is an excluded field, don't add this check.688 if name in exclude:689 break690 else:691 unique_checks.append(tuple(check))692 685 686 unique_togethers = [(self.__class__, self._meta.unique_together)] 687 for parent_class in self._meta.parents.keys(): 688 if parent_class._meta.unique_together: 689 unique_togethers.append((parent_class, parent_class._meta.unique_together)) 690 691 for model_class, unique_together in unique_togethers: 692 for check in unique_together: 693 for name in check: 694 # If this is an excluded field, don't add this check. 695 if name in exclude: 696 break 697 else: 698 unique_checks.append((model_class, tuple(check))) 699 693 700 # These are checks for the unique_for_<date/year/month>. 694 701 date_checks = [] 695 702 696 703 # Gather a list of checks for fields declared as unique and add them to 697 704 # the list of checks. 698 for f in self._meta.fields: 699 name = f.name 700 if name in exclude: 701 continue 702 if f.unique: 703 unique_checks.append((name,)) 704 if f.unique_for_date: 705 date_checks.append(('date', name, f.unique_for_date)) 706 if f.unique_for_year: 707 date_checks.append(('year', name, f.unique_for_year)) 708 if f.unique_for_month: 709 date_checks.append(('month', name, f.unique_for_month)) 705 706 fields_with_class = [(self.__class__, self._meta.local_fields)] 707 for parent_class in self._meta.parents.keys(): 708 fields_with_class.append((parent_class, parent_class._meta.local_fields)) 709 710 for model_class, fields in fields_with_class: 711 for f in fields: 712 name = f.name 713 if name in exclude: 714 continue 715 if f.unique: 716 unique_checks.append((model_class, (name,))) 717 if f.unique_for_date: 718 date_checks.append((model_class, 'date', name, f.unique_for_date)) 719 if f.unique_for_year: 720 date_checks.append((model_class, 'year', name, f.unique_for_year)) 721 if f.unique_for_month: 722 date_checks.append((model_class, 'month', name, f.unique_for_month)) 710 723 return unique_checks, date_checks 711 724 712 725 def _perform_unique_checks(self, unique_checks): 713 726 errors = {} 714 727 715 for unique_check in unique_checks:728 for model_class, unique_check in unique_checks: 716 729 # Try to look up an existing object with the same values as this 717 730 # object's values for all the unique field. 718 731 … … 732 745 if len(unique_check) != len(lookup_kwargs.keys()): 733 746 continue 734 747 735 qs = self.__class__._default_manager.filter(**lookup_kwargs)748 qs = model_class._default_manager.filter(**lookup_kwargs) 736 749 737 750 # Exclude the current object from the query if we are editing an 738 751 # instance (as opposed to creating a new one) … … 744 757 key = unique_check[0] 745 758 else: 746 759 key = NON_FIELD_ERRORS 747 errors.setdefault(key, []).append(self.unique_error_message( unique_check))760 errors.setdefault(key, []).append(self.unique_error_message(model_class, unique_check)) 748 761 749 762 return errors 750 763 751 764 def _perform_date_checks(self, date_checks): 752 765 errors = {} 753 for lookup_type, field, unique_for in date_checks:766 for model_class, lookup_type, field, unique_for in date_checks: 754 767 lookup_kwargs = {} 755 768 # there's a ticket to add a date lookup, we can remove this special 756 769 # case if that makes it's way in … … 763 776 lookup_kwargs['%s__%s' % (unique_for, lookup_type)] = getattr(date, lookup_type) 764 777 lookup_kwargs[field] = getattr(self, field) 765 778 766 qs = self.__class__._default_manager.filter(**lookup_kwargs)779 qs = model_class._default_manager.filter(**lookup_kwargs) 767 780 # Exclude the current object from the query if we are editing an 768 781 # instance (as opposed to creating a new one) 769 782 if not getattr(self, '_adding', False) and self.pk is not None: … … 783 796 'lookup': lookup_type, 784 797 } 785 798 786 def unique_error_message(self, unique_check):787 opts = self._meta799 def unique_error_message(self, model_class, unique_check): 800 opts = model_class._meta 788 801 model_name = capfirst(opts.verbose_name) 789 802 790 803 # A unique field -
django/forms/models.py
488 488 489 489 errors = [] 490 490 # Do each of the unique checks (unique and unique_together) 491 for u nique_check in all_unique_checks:491 for uclass, unique_check in all_unique_checks: 492 492 seen_data = set() 493 493 for form in self.forms: 494 494 # if the form doesn't have cleaned_data then we ignore it, … … 512 512 # iterate over each of the date checks now 513 513 for date_check in all_date_checks: 514 514 seen_data = set() 515 lookup, field, unique_for = date_check515 uclass, lookup, field, unique_for = date_check 516 516 for form in self.forms: 517 517 # if the form doesn't have cleaned_data then we ignore it, 518 518 # it's already invalid … … 556 556 def get_date_error_message(self, date_check): 557 557 return ugettext("Please correct the duplicate data for %(field_name)s " 558 558 "which must be unique for the %(lookup)s in %(date_field)s.") % { 559 'field_name': date_check[ 1],560 'date_field': date_check[ 2],561 'lookup': unicode(date_check[ 0]),559 'field_name': date_check[2], 560 'date_field': date_check[3], 561 'lookup': unicode(date_check[1]), 562 562 } 563 563 564 564 def get_form_error(self): -
tests/modeltests/model_forms/tests.py
1 import datetime 1 2 from django.test import TestCase 2 3 from django import forms 3 from models import Category 4 from models import Category, Writer, Book, DerivedBook, Post 5 from mforms import ProductForm, PriceForm, BookForm, DerivedBookForm, ExplicitPKForm, PostForm, DerivedPostForm 4 6 5 7 6 8 class IncompleteCategoryFormWithFields(forms.ModelForm): … … 35 37 form = IncompleteCategoryFormWithExclude(data={'name': 'some name', 'slug': 'some-slug'}) 36 38 assert form.is_valid() 37 39 40 # unique/unique_together validation 41 class UniqueTest(TestCase): 42 def setUp(self): 43 self.writer = Writer.objects.create(name='Mike Royko') 44 45 def test_simple_unique(self): 46 form = ProductForm({'slug': 'teddy-bear-blue'}) 47 self.assertTrue(form.is_valid()) 48 obj = form.save() 49 form = ProductForm({'slug': 'teddy-bear-blue'}) 50 self.assertEqual(len(form.errors), 1) 51 self.assertEqual(form.errors['slug'], [u'Product with this Slug already exists.']) 52 form = ProductForm({'slug': 'teddy-bear-blue'}, instance=obj) 53 self.assertTrue(form.is_valid()) 54 55 def test_unique_together(self): 56 """ModelForm test of unique_together constraint""" 57 form = PriceForm({'price': '6.00', 'quantity': '1'}) 58 self.assertTrue(form.is_valid()) 59 form.save() 60 form = PriceForm({'price': '6.00', 'quantity': '1'}) 61 self.assertFalse(form.is_valid()) 62 self.assertEqual(len(form.errors), 1) 63 self.assertEqual(form.errors['__all__'], [u'Price with this Price and Quantity already exists.']) 64 65 def test_unique_null(self): 66 title = 'I May Be Wrong But I Doubt It' 67 form = BookForm({'title': title, 'author': self.writer.pk}) 68 self.assertTrue(form.is_valid()) 69 form.save() 70 form = BookForm({'title': title, 'author': self.writer.pk}) 71 self.assertFalse(form.is_valid()) 72 self.assertEqual(len(form.errors), 1) 73 self.assertEqual(form.errors['__all__'], [u'Book with this Title and Author already exists.']) 74 form = BookForm({'title': title}) 75 self.assertTrue(form.is_valid()) 76 form.save() 77 form = BookForm({'title': title}) 78 self.assertTrue(form.is_valid()) 79 80 def test_inherited_unique(self): 81 title = 'Boss' 82 Book.objects.create(title=title, author=self.writer, special_id=1) 83 form = DerivedBookForm({'title': 'Other', 'author': self.writer.pk, 'special_id': u'1', 'isbn': '12345'}) 84 self.assertFalse(form.is_valid()) 85 self.assertEqual(len(form.errors), 1) 86 self.assertEqual(form.errors['special_id'], [u'Book with this Special id already exists.']) 87 88 def test_inherited_unique_together(self): 89 title = 'Boss' 90 form = BookForm({'title': title, 'author': self.writer.pk}) 91 self.assertTrue(form.is_valid()) 92 form.save() 93 form = DerivedBookForm({'title': title, 'author': self.writer.pk, 'isbn': '12345'}) 94 self.assertFalse(form.is_valid()) 95 self.assertEqual(len(form.errors), 1) 96 self.assertEqual(form.errors['__all__'], [u'Book with this Title and Author already exists.']) 97 98 def test_abstract_inherited_unique(self): 99 title = 'Boss' 100 isbn = '12345' 101 dbook = DerivedBook.objects.create(title=title, author=self.writer, isbn=isbn) 102 form = DerivedBookForm({'title': 'Other', 'author': self.writer.pk, 'isbn': isbn}) 103 self.assertFalse(form.is_valid()) 104 self.assertEqual(len(form.errors), 1) 105 self.assertEqual(form.errors['isbn'], [u'Derived book with this Isbn already exists.']) 106 107 def test_abstract_inherited_unique_together(self): 108 title = 'Boss' 109 isbn = '12345' 110 dbook = DerivedBook.objects.create(title=title, author=self.writer, isbn=isbn) 111 form = DerivedBookForm({'title': 'Other', 'author': self.writer.pk, 'isbn': '9876', 'suffix1': u'0', 'suffix2': u'0'}) 112 self.assertFalse(form.is_valid()) 113 self.assertEqual(len(form.errors), 1) 114 self.assertEqual(form.errors['__all__'], [u'Derived book with this Suffix1 and Suffix2 already exists.']) 115 116 def test_explicitpk_unspecified(self): 117 """Test for primary_key being in the form and failing validation.""" 118 form = ExplicitPKForm({'key': u'', 'desc': u'' }) 119 self.assertFalse(form.is_valid()) 120 121 def test_explicitpk_unique(self): 122 """Ensure keys and blank character strings are tested for uniqueness.""" 123 form = ExplicitPKForm({'key': u'key1', 'desc': u''}) 124 self.assertTrue(form.is_valid()) 125 form.save() 126 form = ExplicitPKForm({'key': u'key1', 'desc': u''}) 127 self.assertFalse(form.is_valid()) 128 self.assertEqual(len(form.errors), 3) 129 self.assertEqual(form.errors['__all__'], [u'Explicit pk with this Key and Desc already exists.']) 130 self.assertEqual(form.errors['desc'], [u'Explicit pk with this Desc already exists.']) 131 self.assertEqual(form.errors['key'], [u'Explicit pk with this Key already exists.']) 132 133 def test_unique_for_date(self): 134 p = Post.objects.create(title="Django 1.0 is released", 135 slug="Django 1.0", subtitle="Finally", posted=datetime.date(2008, 9, 3)) 136 form = PostForm({'title': "Django 1.0 is released", 'posted': '2008-09-03'}) 137 self.assertFalse(form.is_valid()) 138 self.assertEqual(len(form.errors), 1) 139 self.assertEqual(form.errors['title'], [u'Title must be unique for Posted date.']) 140 form = PostForm({'title': "Work on Django 1.1 begins", 'posted': '2008-09-03'}) 141 self.assertTrue(form.is_valid()) 142 form = PostForm({'title': "Django 1.0 is released", 'posted': '2008-09-04'}) 143 self.assertTrue(form.is_valid()) 144 form = PostForm({'slug': "Django 1.0", 'posted': '2008-01-01'}) 145 self.assertFalse(form.is_valid()) 146 self.assertEqual(len(form.errors), 1) 147 self.assertEqual(form.errors['slug'], [u'Slug must be unique for Posted year.']) 148 form = PostForm({'subtitle': "Finally", 'posted': '2008-09-30'}) 149 self.assertFalse(form.is_valid()) 150 self.assertEqual(form.errors['subtitle'], [u'Subtitle must be unique for Posted month.']) 151 form = PostForm({'subtitle': "Finally", "title": "Django 1.0 is released", 152 "slug": "Django 1.0", 'posted': '2008-09-03'}, instance=p) 153 self.assertTrue(form.is_valid()) 154 155 def test_inherited_unique_for_date(self): 156 p = Post.objects.create(title="Django 1.0 is released", 157 slug="Django 1.0", subtitle="Finally", posted=datetime.date(2008, 9, 3)) 158 form = DerivedPostForm({'title': "Django 1.0 is released", 'posted': '2008-09-03'}) 159 self.assertFalse(form.is_valid()) 160 self.assertEqual(len(form.errors), 1) 161 self.assertEqual(form.errors['title'], [u'Title must be unique for Posted date.']) 162 form = DerivedPostForm({'title': "Work on Django 1.1 begins", 'posted': '2008-09-03'}) 163 self.assertTrue(form.is_valid()) 164 form = DerivedPostForm({'title': "Django 1.0 is released", 'posted': '2008-09-04'}) 165 self.assertTrue(form.is_valid()) 166 form = DerivedPostForm({'slug': "Django 1.0", 'posted': '2008-01-01'}) 167 self.assertFalse(form.is_valid()) 168 self.assertEqual(len(form.errors), 1) 169 self.assertEqual(form.errors['slug'], [u'Slug must be unique for Posted year.']) 170 form = DerivedPostForm({'subtitle': "Finally", 'posted': '2008-09-30'}) 171 self.assertFalse(form.is_valid()) 172 self.assertEqual(form.errors['subtitle'], [u'Subtitle must be unique for Posted month.']) 173 form = DerivedPostForm({'subtitle': "Finally", "title": "Django 1.0 is released", 174 "slug": "Django 1.0", 'posted': '2008-09-03'}, instance=p) 175 self.assertTrue(form.is_valid()) 176 -
tests/modeltests/model_forms/models.py
101 101 from PIL import Image, _imaging 102 102 except ImportError: 103 103 import Image, _imaging 104 104 105 105 test_images = True 106 106 107 107 class ImageFile(models.Model): … … 181 181 class Meta: 182 182 unique_together = ('title', 'author') 183 183 184 class BookXtra(models.Model): 185 isbn = models.CharField(max_length=16, unique=True) 186 suffix1 = models.IntegerField(blank=True, default=0) 187 suffix2 = models.IntegerField(blank=True, default=0) 188 189 class Meta: 190 unique_together = (('suffix1', 'suffix2')) 191 abstract = True 192 193 class DerivedBook(Book, BookXtra): 194 pass 195 184 196 class ExplicitPK(models.Model): 185 197 key = models.CharField(max_length=20, primary_key=True) 186 198 desc = models.CharField(max_length=20, blank=True, unique=True) … … 199 211 def __unicode__(self): 200 212 return self.name 201 213 214 class DerivedPost(Post): 215 pass 216 202 217 class BigInt(models.Model): 203 218 biggie = models.BigIntegerField() 204 219 … … 1424 1439 >>> f.cleaned_data 1425 1440 {'field': u'1'} 1426 1441 1427 # unique/unique_together validation1428 1429 >>> class ProductForm(ModelForm):1430 ... class Meta:1431 ... model = Product1432 >>> form = ProductForm({'slug': 'teddy-bear-blue'})1433 >>> form.is_valid()1434 True1435 >>> obj = form.save()1436 >>> obj1437 <Product: teddy-bear-blue>1438 >>> form = ProductForm({'slug': 'teddy-bear-blue'})1439 >>> form.is_valid()1440 False1441 >>> form._errors1442 {'slug': [u'Product with this Slug already exists.']}1443 >>> form = ProductForm({'slug': 'teddy-bear-blue'}, instance=obj)1444 >>> form.is_valid()1445 True1446 1447 # ModelForm test of unique_together constraint1448 >>> class PriceForm(ModelForm):1449 ... class Meta:1450 ... model = Price1451 >>> form = PriceForm({'price': '6.00', 'quantity': '1'})1452 >>> form.is_valid()1453 True1454 >>> form.save()1455 <Price: 1 for 6.00>1456 >>> form = PriceForm({'price': '6.00', 'quantity': '1'})1457 >>> form.is_valid()1458 False1459 >>> form._errors1460 {'__all__': [u'Price with this Price and Quantity already exists.']}1461 1462 1442 This Price instance generated by this form is not valid because the quantity 1463 1443 field is required, but the form is valid because the field is excluded from 1464 1444 the form. This is for backwards compatibility. … … 1495 1475 >>> form.instance.pk is None 1496 1476 True 1497 1477 1498 # Unique & unique together with null values1499 >>> class BookForm(ModelForm):1500 ... class Meta:1501 ... model = Book1502 >>> w = Writer.objects.get(name='Mike Royko')1503 >>> form = BookForm({'title': 'I May Be Wrong But I Doubt It', 'author' : w.pk})1504 >>> form.is_valid()1505 True1506 >>> form.save()1507 <Book: Book object>1508 >>> form = BookForm({'title': 'I May Be Wrong But I Doubt It', 'author' : w.pk})1509 >>> form.is_valid()1510 False1511 >>> form._errors1512 {'__all__': [u'Book with this Title and Author already exists.']}1513 >>> form = BookForm({'title': 'I May Be Wrong But I Doubt It'})1514 >>> form.is_valid()1515 True1516 >>> form.save()1517 <Book: Book object>1518 >>> form = BookForm({'title': 'I May Be Wrong But I Doubt It'})1519 >>> form.is_valid()1520 True1521 1522 # Test for primary_key being in the form and failing validation.1523 >>> class ExplicitPKForm(ModelForm):1524 ... class Meta:1525 ... model = ExplicitPK1526 ... fields = ('key', 'desc',)1527 >>> form = ExplicitPKForm({'key': u'', 'desc': u'' })1528 >>> form.is_valid()1529 False1530 1531 # Ensure keys and blank character strings are tested for uniqueness.1532 >>> form = ExplicitPKForm({'key': u'key1', 'desc': u''})1533 >>> form.is_valid()1534 True1535 >>> form.save()1536 <ExplicitPK: key1>1537 >>> form = ExplicitPKForm({'key': u'key1', 'desc': u''})1538 >>> form.is_valid()1539 False1540 >>> sorted(form.errors.items())1541 [('__all__', [u'Explicit pk with this Key and Desc already exists.']), ('desc', [u'Explicit pk with this Desc already exists.']), ('key', [u'Explicit pk with this Key already exists.'])]1542 1543 1478 # Choices on CharField and IntegerField 1544 1479 >>> class ArticleForm(ModelForm): 1545 1480 ... class Meta: … … 1605 1540 <tr><th><label for="id_description">Description:</label></th><td><input type="text" name="description" id="id_description" /></td></tr> 1606 1541 <tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr> 1607 1542 1608 ### Validation on unique_for_date1609 1610 >>> p = Post.objects.create(title="Django 1.0 is released", slug="Django 1.0", subtitle="Finally", posted=datetime.date(2008, 9, 3))1611 >>> class PostForm(ModelForm):1612 ... class Meta:1613 ... model = Post1614 1615 >>> f = PostForm({'title': "Django 1.0 is released", 'posted': '2008-09-03'})1616 >>> f.is_valid()1617 False1618 >>> f.errors1619 {'title': [u'Title must be unique for Posted date.']}1620 >>> f = PostForm({'title': "Work on Django 1.1 begins", 'posted': '2008-09-03'})1621 >>> f.is_valid()1622 True1623 >>> f = PostForm({'title': "Django 1.0 is released", 'posted': '2008-09-04'})1624 >>> f.is_valid()1625 True1626 >>> f = PostForm({'slug': "Django 1.0", 'posted': '2008-01-01'})1627 >>> f.is_valid()1628 False1629 >>> f.errors1630 {'slug': [u'Slug must be unique for Posted year.']}1631 >>> f = PostForm({'subtitle': "Finally", 'posted': '2008-09-30'})1632 >>> f.is_valid()1633 False1634 >>> f.errors1635 {'subtitle': [u'Subtitle must be unique for Posted month.']}1636 >>> f = PostForm({'subtitle': "Finally", "title": "Django 1.0 is released", "slug": "Django 1.0", 'posted': '2008-09-03'}, instance=p)1637 >>> f.is_valid()1638 True1639 1640 1543 # Clean up 1641 1544 >>> import shutil 1642 1545 >>> shutil.rmtree(temp_storage_dir) -
tests/modeltests/model_forms/mforms.py
1 from django.forms import ModelForm 2 3 from models import Product, Price, Book, DerivedBook, ExplicitPK, Post, DerivedPost 4 5 class ProductForm(ModelForm): 6 class Meta: 7 model = Product 8 9 class PriceForm(ModelForm): 10 class Meta: 11 model = Price 12 13 class BookForm(ModelForm): 14 class Meta: 15 model = Book 16 17 class DerivedBookForm(ModelForm): 18 class Meta: 19 model = DerivedBook 20 21 class ExplicitPKForm(ModelForm): 22 class Meta: 23 model = ExplicitPK 24 fields = ('key', 'desc',) 25 26 class PostForm(ModelForm): 27 class Meta: 28 model = Post 29 30 class DerivedPostForm(ModelForm): 31 class Meta: 32 model = DerivedPost -
tests/modeltests/validation/test_unique.py
Property changes on: tests/modeltests/model_forms/mforms.py ___________________________________________________________________ Added: svn:executable + * Added: svn:eol-style + native
9 9 def test_unique_fields_get_collected(self): 10 10 m = UniqueFieldsModel() 11 11 self.assertEqual( 12 ([('id',), ('unique_charfield',), ('unique_integerfield',)], []), 12 ([(UniqueFieldsModel, ('id',)), 13 (UniqueFieldsModel, ('unique_charfield',)), 14 (UniqueFieldsModel, ('unique_integerfield',))], 15 []), 13 16 m._get_unique_checks() 14 17 ) 15 18 16 19 def test_unique_together_gets_picked_up_and_converted_to_tuple(self): 17 20 m = UniqueTogetherModel() 18 21 self.assertEqual( 19 ([('ifield', 'cfield',),('ifield', 'efield'), ('id',), ], []), 22 ([(UniqueTogetherModel, ('ifield', 'cfield',)), 23 (UniqueTogetherModel, ('ifield', 'efield')), 24 (UniqueTogetherModel, ('id',)), ], 25 []), 20 26 m._get_unique_checks() 21 27 ) 22 28 23 29 def test_primary_key_is_considered_unique(self): 24 30 m = CustomPKModel() 25 self.assertEqual(([( 'my_pk_field',)], []), m._get_unique_checks())31 self.assertEqual(([(CustomPKModel, ('my_pk_field',))], []), m._get_unique_checks()) 26 32 27 33 def test_unique_for_date_gets_picked_up(self): 28 34 m = UniqueForDateModel() 29 35 self.assertEqual(( 30 [('id',)], 31 [('date', 'count', 'start_date'), ('year', 'count', 'end_date'), ('month', 'order', 'end_date')] 36 [(UniqueForDateModel, ('id',))], 37 [(UniqueForDateModel, 'date', 'count', 'start_date'), 38 (UniqueForDateModel, 'year', 'count', 'end_date'), 39 (UniqueForDateModel, 'month', 'order', 'end_date')] 32 40 ), m._get_unique_checks() 33 41 ) 34 42