Ticket #13091: 13091_partial_unique_together.3.diff
File 13091_partial_unique_together.3.diff, 14.2 KB (added by , 14 years ago) |
---|
-
docs/ref/models/instances.txt
113 113 114 114 Finally, ``full_clean()`` will check any unique constraints on your model. 115 115 116 .. _model-validate-unique 117 116 118 .. method:: Model.validate_unique(exclude=None) 117 119 120 .. versionadded:: 1.4 121 Prior to Django 1.4, a ``unique_together`` constraint would be ignored if any of the fields were listed in ``exclude`` 122 118 123 This method is similar to ``clean_fields``, but validates all uniqueness 119 124 constraints on your model instead of individual field values. The optional 120 125 ``exclude`` argument allows you to provide a list of field names to exclude … … 122 127 validation. 123 128 124 129 Note that if you provide an ``exclude`` argument to ``validate_unique``, any 125 ``unique_together`` constraint that contains one of the fields you provided 126 will not be checked. 130 ``unique_together`` constraint that has all of its fields listed in ``exclude`` 131 will be ignored. An exception to this is the case of a field with its ``default`` 132 parameter defined. If such a default-enabled field is listed in ``exclude``, all 133 ``unique_together`` constraints that reference that field will also be ignored. 127 134 128 135 136 129 137 Saving objects 130 138 ============== 131 139 -
docs/ref/models/options.txt
231 231 This is a list of lists of fields that must be unique when considered together. 232 232 It's used in the Django admin and is enforced at the database level (i.e., the 233 233 appropriate ``UNIQUE`` statements are included in the ``CREATE TABLE`` 234 statement). 234 statement). As of Django 1.2, it is also used in ModelForm's 235 :ref:`model validation <model-validate-unique>`. 235 236 236 237 For convenience, unique_together can be a single list when dealing with a single 237 238 set of fields:: 238 239 239 240 unique_together = ("driver", "restaurant") 240 241 242 241 243 ``verbose_name`` 242 244 ---------------- 243 245 -
django/db/models/base.py
7 7 import django.db.models.manager # Imported to register signal handler. 8 8 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS 9 9 from django.core import validators 10 from django.db.models.fields import AutoField, FieldDoesNotExist 10 from django.db.models.fields import AutoField, FieldDoesNotExist, NOT_PROVIDED 11 11 from django.db.models.fields.related import (OneToOneRel, ManyToOneRel, 12 12 OneToOneField, add_lazy_relation) 13 13 from django.db.models.query import Q … … 667 667 668 668 for model_class, unique_together in unique_togethers: 669 669 for check in unique_together: 670 # if all fields are excluded, do not add to unique_checks 671 check_count = len(check) 670 672 for name in check: 671 # If this is an excluded field, don't add this check.672 673 if name in exclude: 673 break 674 check_count -= 1 675 if check_count > 0: 676 # if the default value is specified, do not add to unique_checks 677 default = self._meta.fields_dict[name].default 678 if default is not NOT_PROVIDED: 679 break 680 else: 681 break 674 682 else: 675 683 unique_checks.append((model_class, tuple(check))) 676 684 -
django/db/models/options.py
219 219 return self._field_name_cache 220 220 fields = property(_fields) 221 221 222 def _fields_dict(self): 223 try: 224 return self._fields_dict_cache 225 except AttributeError: 226 self._fields_dict_cache = dict([(field.name, field) for field in self.fields]) 227 return self._fields_dict_cache 228 fields_dict = property(_fields_dict) 229 222 230 def get_fields_with_model(self): 223 231 """ 224 232 Returns a sequence of (field, model) pairs for all fields. The "model" -
tests/modeltests/model_forms/tests.py
4 4 from models import Category, Writer, Book, DerivedBook, Post, FlexibleDatePost 5 5 from mforms import (ProductForm, PriceForm, BookForm, DerivedBookForm, 6 6 ExplicitPKForm, PostForm, DerivedPostForm, CustomWriterForm, 7 FlexDatePostForm) 7 FlexDatePostForm, BirthdayPresentForm, DefaultBirthdayPresentForm, 8 ExcludeAllBirthdayPresentForm) 8 9 9 10 10 11 class IncompleteCategoryFormWithFields(forms.ModelForm): … … 68 69 self.assertEqual(len(form.errors), 1) 69 70 self.assertEqual(form.errors['__all__'], [u'Price with this Price and Quantity already exists.']) 70 71 72 def test_excluded_unique_together(self): 73 """ 74 ModelForm test of unique_together where a unique_together 75 field is excluded from the form 76 """ 77 form = BirthdayPresentForm({'year': '1998', 'description':'a blue bicycle'}) 78 form.instance.username = 'joe.smith' 79 self.assertTrue(form.is_valid()) 80 form.save() 81 form = BirthdayPresentForm({'year': '1998', 'description':'a sweater'}) 82 form.instance.username = 'joe.smith' 83 self.assertFalse(form.is_valid()) 84 self.assertEqual(len(form.errors), 1) 85 self.assertEqual(form.errors['__all__'], [u'Birthday present with this Username and Year already exists.']) 86 87 def test_excluded_unique_together_default(self): 88 """ 89 ModelForm test of unique_together where a unique_together 90 field with a default value is excluded from the form 91 """ 92 form = DefaultBirthdayPresentForm({'year': '2000', 'description':'a blue bicycle'}) 93 self.assertTrue(form.is_valid()) 94 form.save() 95 form = DefaultBirthdayPresentForm({'year': '2000', 'description':'a sweater'}) 96 self.assertTrue(form.is_valid()) 97 98 def test_excluded_unique_together_all(self): 99 """ 100 ModelForm test of unique_together where all specified 101 unique_together fields are excluded from the form 102 """ 103 form = ExcludeAllBirthdayPresentForm({'description':'a rock'}) 104 form.instance.year = '2010' 105 form.instance.username = 'fred.wilson' 106 self.assertTrue(form.is_valid()) 107 form.save() 108 form = ExcludeAllBirthdayPresentForm({'description':'an empty box'}) 109 form.instance.year = '2010' 110 form.instance.username = 'fred.wilson' 111 self.assertTrue(form.is_valid()) 112 71 113 def test_unique_null(self): 72 114 title = 'I May Be Wrong But I Doubt It' 73 115 form = BookForm({'title': title, 'author': self.writer.pk}) -
tests/modeltests/model_forms/mforms.py
2 2 from django.forms import ModelForm 3 3 4 4 from models import (Product, Price, Book, DerivedBook, ExplicitPK, Post, 5 DerivedPost, Writer, FlexibleDatePost) 5 DerivedPost, Writer, FlexibleDatePost, BirthdayPresent, 6 DefaultBirthdayPresent) 6 7 7 8 class ProductForm(ModelForm): 8 9 class Meta: … … 42 43 class FlexDatePostForm(ModelForm): 43 44 class Meta: 44 45 model = FlexibleDatePost 46 47 class BirthdayPresentForm(ModelForm): 48 class Meta: 49 model = BirthdayPresent 50 exclude = ('username',) 51 52 class DefaultBirthdayPresentForm(ModelForm): 53 class Meta: 54 model = DefaultBirthdayPresent 55 exclude = ('username',) 56 57 class ExcludeAllBirthdayPresentForm(ModelForm): 58 class Meta: 59 model = BirthdayPresent 60 exclude = ('username', 'year',) 61 -
tests/modeltests/model_forms/models.py
248 248 subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True) 249 249 posted = models.DateField(blank=True, null=True) 250 250 251 class BirthdayPresent(models.Model): 252 """used to test unique_together validation""" 253 username = models.CharField(max_length=30) 254 year = models.IntegerField() 255 description = models.CharField(max_length=200) 256 257 class Meta: 258 unique_together = ('username', 'year') 259 260 class DefaultBirthdayPresent(models.Model): 261 """used to test unique_together validation""" 262 username = models.CharField(max_length=30, default='jeremy.jones') 263 year = models.IntegerField() 264 description = models.CharField(max_length=200) 265 266 class Meta: 267 unique_together = (('username', 'year'),) 268 269 251 270 __test__ = {'API_TESTS': """ 252 271 >>> from django import forms 253 272 >>> from django.forms.models import ModelForm, model_to_dict -
tests/modeltests/model_formsets/tests.py
1066 1066 self.assertEqual(formset._non_form_errors, 1067 1067 [u'Please correct the duplicate data for price and quantity, which must be unique.']) 1068 1068 1069 # Only the price field is specified, this should skip anyunique checks since1070 # the unique_together is not fulfilled. This will fail with a KeyError if broken.1069 # Only the price field is specified, this will fail in the unique checks since 1070 # the unique_together is partially included. 1071 1071 FormSet = modelformset_factory(Price, fields=("price",), extra=2) 1072 1072 data = { 1073 1073 'form-TOTAL_FORMS': '2', … … 1077 1077 'form-1-price': '24', 1078 1078 } 1079 1079 formset = FormSet(data) 1080 self.assertTrue(formset.is_valid()) 1080 self.assertFalse(formset.is_valid()) 1081 self.assertEqual(formset._non_form_errors, 1082 [u'Please correct the duplicate data for price and quantity, which must be unique.']) 1081 1083 1082 1084 FormSet = inlineformset_factory(Author, Book, extra=0) 1083 1085 author = Author.objects.create(pk=1, name='Charles Baudelaire') -
tests/regressiontests/admin_views/tests.py
36 36 Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast, 37 37 Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit, 38 38 Category, Post, Plot, FunkyTag, Chapter, Book, Promo, WorkHour, Employee, 39 Question, Answer, Inquisition, Actor, FoodDelivery, 39 Question, Answer, Inquisition, Actor, FoodDelivery, UniqueTogether, 40 40 RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory) 41 41 42 42 … … 1403 1403 # 1 select per object = 3 selects 1404 1404 self.assertEqual(response.content.count("<select"), 4) 1405 1405 1406 def test_partial_unique_together(self): 1407 """ Ensure that no IntegrityError is raised when editing some (but 1408 not all) of the values specified as unique_together. Refs #13091. 1409 """ 1410 UniqueTogether.objects.create(t1='a', t2='b', t3='c') 1411 UniqueTogether.objects.create(t1='b', t2='b', t3='a') 1412 UniqueTogether.objects.create(t1='c', t2='a', t3='c') 1413 data = { 1414 "form-TOTAL_FORMS": "3", 1415 "form-INITIAL_FORMS": "3", 1416 "form-MAX_NUM_FORMS": "0", 1417 1418 "form-0-t1": "a", 1419 "form-0-t2": "b", 1420 "form-0-t3": "c", 1421 "form-0-id": "1", 1422 1423 "form-1-t1": "a", 1424 "form-1-t2": "b", 1425 "form-1-t3": "c", 1426 "form-1-id": "2", 1427 1428 "form-2-t1": "a", 1429 "form-2-t2": "b", 1430 "form-2-t3": "c", 1431 "form-2-id": "3", 1432 1433 "_save": "Save", 1434 } 1435 response = self.client.post('/test_admin/admin/admin_views/uniquetogether/', data) 1436 self.assertContains(response, 'Please correct the errors below.') 1437 self.assertContains(response, 'Unique together with this T1, T2 and T3 already exists.') 1438 1406 1439 def test_post_messages(self): 1407 1440 # Ticket 12707: Saving inline editable should not show admin 1408 1441 # action warnings -
tests/regressiontests/admin_views/models.py
803 803 list_display_links = ('title', 'id') # 'id' in list_display_links 804 804 list_editable = ('content', ) 805 805 806 class UniqueTogether(models.Model): 807 t1 = models.CharField(max_length=255, blank=True, null=True) 808 t2 = models.CharField(max_length=255, blank=True, null=True) 809 t3 = models.CharField(max_length=255, blank=True, null=True) 810 811 class Meta: 812 unique_together = ['t1','t2','t3'] 813 814 class UniqueTogetherAdmin(admin.ModelAdmin): 815 list_display = ['t3', 't1', 't2'] 816 list_editable = ['t1', 't2',] 817 818 806 819 admin.site.register(Article, ArticleAdmin) 807 820 admin.site.register(CustomArticle, CustomArticleAdmin) 808 821 admin.site.register(Section, save_as=True, inlines=[ArticleInline]) … … 845 858 admin.site.register(CoverLetter, CoverLetterAdmin) 846 859 admin.site.register(Story, StoryAdmin) 847 860 admin.site.register(OtherStory, OtherStoryAdmin) 861 admin.site.register(UniqueTogether, UniqueTogetherAdmin) 848 862 849 863 # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. 850 864 # That way we cover all four cases: