Fixed #12881: Corrected handling of inherited unique constraints. Thanks for report fgaudin.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12797 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
883329ecb3
commit
47a822207f
|
@ -707,37 +707,50 @@ class Model(object):
|
||||||
if exclude is None:
|
if exclude is None:
|
||||||
exclude = []
|
exclude = []
|
||||||
unique_checks = []
|
unique_checks = []
|
||||||
for check in self._meta.unique_together:
|
|
||||||
for name in check:
|
unique_togethers = [(self.__class__, self._meta.unique_together)]
|
||||||
# If this is an excluded field, don't add this check.
|
for parent_class in self._meta.parents.keys():
|
||||||
if name in exclude:
|
if parent_class._meta.unique_together:
|
||||||
break
|
unique_togethers.append((parent_class, parent_class._meta.unique_together))
|
||||||
else:
|
|
||||||
unique_checks.append(tuple(check))
|
for model_class, unique_together in unique_togethers:
|
||||||
|
for check in unique_together:
|
||||||
|
for name in check:
|
||||||
|
# If this is an excluded field, don't add this check.
|
||||||
|
if name in exclude:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
unique_checks.append((model_class, tuple(check)))
|
||||||
|
|
||||||
# These are checks for the unique_for_<date/year/month>.
|
# These are checks for the unique_for_<date/year/month>.
|
||||||
date_checks = []
|
date_checks = []
|
||||||
|
|
||||||
# Gather a list of checks for fields declared as unique and add them to
|
# Gather a list of checks for fields declared as unique and add them to
|
||||||
# the list of checks.
|
# the list of checks.
|
||||||
for f in self._meta.fields:
|
|
||||||
name = f.name
|
fields_with_class = [(self.__class__, self._meta.local_fields)]
|
||||||
if name in exclude:
|
for parent_class in self._meta.parents.keys():
|
||||||
continue
|
fields_with_class.append((parent_class, parent_class._meta.local_fields))
|
||||||
if f.unique:
|
|
||||||
unique_checks.append((name,))
|
for model_class, fields in fields_with_class:
|
||||||
if f.unique_for_date:
|
for f in fields:
|
||||||
date_checks.append(('date', name, f.unique_for_date))
|
name = f.name
|
||||||
if f.unique_for_year:
|
if name in exclude:
|
||||||
date_checks.append(('year', name, f.unique_for_year))
|
continue
|
||||||
if f.unique_for_month:
|
if f.unique:
|
||||||
date_checks.append(('month', name, f.unique_for_month))
|
unique_checks.append((model_class, (name,)))
|
||||||
|
if f.unique_for_date:
|
||||||
|
date_checks.append((model_class, 'date', name, f.unique_for_date))
|
||||||
|
if f.unique_for_year:
|
||||||
|
date_checks.append((model_class, 'year', name, f.unique_for_year))
|
||||||
|
if f.unique_for_month:
|
||||||
|
date_checks.append((model_class, 'month', name, f.unique_for_month))
|
||||||
return unique_checks, date_checks
|
return unique_checks, date_checks
|
||||||
|
|
||||||
def _perform_unique_checks(self, unique_checks):
|
def _perform_unique_checks(self, unique_checks):
|
||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
for unique_check in unique_checks:
|
for model_class, unique_check in unique_checks:
|
||||||
# Try to look up an existing object with the same values as this
|
# Try to look up an existing object with the same values as this
|
||||||
# object's values for all the unique field.
|
# object's values for all the unique field.
|
||||||
|
|
||||||
|
@ -757,7 +770,7 @@ class Model(object):
|
||||||
if len(unique_check) != len(lookup_kwargs.keys()):
|
if len(unique_check) != len(lookup_kwargs.keys()):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
qs = self.__class__._default_manager.filter(**lookup_kwargs)
|
qs = model_class._default_manager.filter(**lookup_kwargs)
|
||||||
|
|
||||||
# Exclude the current object from the query if we are editing an
|
# Exclude the current object from the query if we are editing an
|
||||||
# instance (as opposed to creating a new one)
|
# instance (as opposed to creating a new one)
|
||||||
|
@ -769,13 +782,13 @@ class Model(object):
|
||||||
key = unique_check[0]
|
key = unique_check[0]
|
||||||
else:
|
else:
|
||||||
key = NON_FIELD_ERRORS
|
key = NON_FIELD_ERRORS
|
||||||
errors.setdefault(key, []).append(self.unique_error_message(unique_check))
|
errors.setdefault(key, []).append(self.unique_error_message(model_class, unique_check))
|
||||||
|
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
def _perform_date_checks(self, date_checks):
|
def _perform_date_checks(self, date_checks):
|
||||||
errors = {}
|
errors = {}
|
||||||
for lookup_type, field, unique_for in date_checks:
|
for model_class, lookup_type, field, unique_for in date_checks:
|
||||||
lookup_kwargs = {}
|
lookup_kwargs = {}
|
||||||
# there's a ticket to add a date lookup, we can remove this special
|
# there's a ticket to add a date lookup, we can remove this special
|
||||||
# case if that makes it's way in
|
# case if that makes it's way in
|
||||||
|
@ -788,7 +801,7 @@ class Model(object):
|
||||||
lookup_kwargs['%s__%s' % (unique_for, lookup_type)] = getattr(date, lookup_type)
|
lookup_kwargs['%s__%s' % (unique_for, lookup_type)] = getattr(date, lookup_type)
|
||||||
lookup_kwargs[field] = getattr(self, field)
|
lookup_kwargs[field] = getattr(self, field)
|
||||||
|
|
||||||
qs = self.__class__._default_manager.filter(**lookup_kwargs)
|
qs = model_class._default_manager.filter(**lookup_kwargs)
|
||||||
# Exclude the current object from the query if we are editing an
|
# Exclude the current object from the query if we are editing an
|
||||||
# instance (as opposed to creating a new one)
|
# instance (as opposed to creating a new one)
|
||||||
if not getattr(self, '_adding', False) and self.pk is not None:
|
if not getattr(self, '_adding', False) and self.pk is not None:
|
||||||
|
@ -808,8 +821,8 @@ class Model(object):
|
||||||
'lookup': lookup_type,
|
'lookup': lookup_type,
|
||||||
}
|
}
|
||||||
|
|
||||||
def unique_error_message(self, unique_check):
|
def unique_error_message(self, model_class, unique_check):
|
||||||
opts = self._meta
|
opts = model_class._meta
|
||||||
model_name = capfirst(opts.verbose_name)
|
model_name = capfirst(opts.verbose_name)
|
||||||
|
|
||||||
# A unique field
|
# A unique field
|
||||||
|
|
|
@ -488,7 +488,7 @@ class BaseModelFormSet(BaseFormSet):
|
||||||
|
|
||||||
errors = []
|
errors = []
|
||||||
# Do each of the unique checks (unique and unique_together)
|
# Do each of the unique checks (unique and unique_together)
|
||||||
for unique_check in all_unique_checks:
|
for uclass, unique_check in all_unique_checks:
|
||||||
seen_data = set()
|
seen_data = set()
|
||||||
for form in self.forms:
|
for form in self.forms:
|
||||||
# if the form doesn't have cleaned_data then we ignore it,
|
# if the form doesn't have cleaned_data then we ignore it,
|
||||||
|
@ -512,7 +512,7 @@ class BaseModelFormSet(BaseFormSet):
|
||||||
# iterate over each of the date checks now
|
# iterate over each of the date checks now
|
||||||
for date_check in all_date_checks:
|
for date_check in all_date_checks:
|
||||||
seen_data = set()
|
seen_data = set()
|
||||||
lookup, field, unique_for = date_check
|
uclass, lookup, field, unique_for = date_check
|
||||||
for form in self.forms:
|
for form in self.forms:
|
||||||
# if the form doesn't have cleaned_data then we ignore it,
|
# if the form doesn't have cleaned_data then we ignore it,
|
||||||
# it's already invalid
|
# it's already invalid
|
||||||
|
@ -556,9 +556,9 @@ class BaseModelFormSet(BaseFormSet):
|
||||||
def get_date_error_message(self, date_check):
|
def get_date_error_message(self, date_check):
|
||||||
return ugettext("Please correct the duplicate data for %(field_name)s "
|
return ugettext("Please correct the duplicate data for %(field_name)s "
|
||||||
"which must be unique for the %(lookup)s in %(date_field)s.") % {
|
"which must be unique for the %(lookup)s in %(date_field)s.") % {
|
||||||
'field_name': date_check[1],
|
'field_name': date_check[2],
|
||||||
'date_field': date_check[2],
|
'date_field': date_check[3],
|
||||||
'lookup': unicode(date_check[0]),
|
'lookup': unicode(date_check[1]),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_form_error(self):
|
def get_form_error(self):
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
from django.forms import ModelForm
|
||||||
|
|
||||||
|
from models import Product, Price, Book, DerivedBook, ExplicitPK, Post, DerivedPost
|
||||||
|
|
||||||
|
class ProductForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Product
|
||||||
|
|
||||||
|
class PriceForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Price
|
||||||
|
|
||||||
|
class BookForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Book
|
||||||
|
|
||||||
|
class DerivedBookForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = DerivedBook
|
||||||
|
|
||||||
|
class ExplicitPKForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = ExplicitPK
|
||||||
|
fields = ('key', 'desc',)
|
||||||
|
|
||||||
|
class PostForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Post
|
||||||
|
|
||||||
|
class DerivedPostForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = DerivedPost
|
|
@ -101,7 +101,7 @@ try:
|
||||||
from PIL import Image, _imaging
|
from PIL import Image, _imaging
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import Image, _imaging
|
import Image, _imaging
|
||||||
|
|
||||||
test_images = True
|
test_images = True
|
||||||
|
|
||||||
class ImageFile(models.Model):
|
class ImageFile(models.Model):
|
||||||
|
@ -181,6 +181,18 @@ class Book(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('title', 'author')
|
unique_together = ('title', 'author')
|
||||||
|
|
||||||
|
class BookXtra(models.Model):
|
||||||
|
isbn = models.CharField(max_length=16, unique=True)
|
||||||
|
suffix1 = models.IntegerField(blank=True, default=0)
|
||||||
|
suffix2 = models.IntegerField(blank=True, default=0)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (('suffix1', 'suffix2'))
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
class DerivedBook(Book, BookXtra):
|
||||||
|
pass
|
||||||
|
|
||||||
class ExplicitPK(models.Model):
|
class ExplicitPK(models.Model):
|
||||||
key = models.CharField(max_length=20, primary_key=True)
|
key = models.CharField(max_length=20, primary_key=True)
|
||||||
desc = models.CharField(max_length=20, blank=True, unique=True)
|
desc = models.CharField(max_length=20, blank=True, unique=True)
|
||||||
|
@ -199,6 +211,9 @@ class Post(models.Model):
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class DerivedPost(Post):
|
||||||
|
pass
|
||||||
|
|
||||||
class BigInt(models.Model):
|
class BigInt(models.Model):
|
||||||
biggie = models.BigIntegerField()
|
biggie = models.BigIntegerField()
|
||||||
|
|
||||||
|
@ -1424,41 +1439,6 @@ True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
{'field': u'1'}
|
{'field': u'1'}
|
||||||
|
|
||||||
# unique/unique_together validation
|
|
||||||
|
|
||||||
>>> class ProductForm(ModelForm):
|
|
||||||
... class Meta:
|
|
||||||
... model = Product
|
|
||||||
>>> form = ProductForm({'slug': 'teddy-bear-blue'})
|
|
||||||
>>> form.is_valid()
|
|
||||||
True
|
|
||||||
>>> obj = form.save()
|
|
||||||
>>> obj
|
|
||||||
<Product: teddy-bear-blue>
|
|
||||||
>>> form = ProductForm({'slug': 'teddy-bear-blue'})
|
|
||||||
>>> form.is_valid()
|
|
||||||
False
|
|
||||||
>>> form._errors
|
|
||||||
{'slug': [u'Product with this Slug already exists.']}
|
|
||||||
>>> form = ProductForm({'slug': 'teddy-bear-blue'}, instance=obj)
|
|
||||||
>>> form.is_valid()
|
|
||||||
True
|
|
||||||
|
|
||||||
# ModelForm test of unique_together constraint
|
|
||||||
>>> class PriceForm(ModelForm):
|
|
||||||
... class Meta:
|
|
||||||
... model = Price
|
|
||||||
>>> form = PriceForm({'price': '6.00', 'quantity': '1'})
|
|
||||||
>>> form.is_valid()
|
|
||||||
True
|
|
||||||
>>> form.save()
|
|
||||||
<Price: 1 for 6.00>
|
|
||||||
>>> form = PriceForm({'price': '6.00', 'quantity': '1'})
|
|
||||||
>>> form.is_valid()
|
|
||||||
False
|
|
||||||
>>> form._errors
|
|
||||||
{'__all__': [u'Price with this Price and Quantity already exists.']}
|
|
||||||
|
|
||||||
This Price instance generated by this form is not valid because the quantity
|
This Price instance generated by this form is not valid because the quantity
|
||||||
field is required, but the form is valid because the field is excluded from
|
field is required, but the form is valid because the field is excluded from
|
||||||
the form. This is for backwards compatibility.
|
the form. This is for backwards compatibility.
|
||||||
|
@ -1495,51 +1475,6 @@ True
|
||||||
>>> form.instance.pk is None
|
>>> form.instance.pk is None
|
||||||
True
|
True
|
||||||
|
|
||||||
# Unique & unique together with null values
|
|
||||||
>>> class BookForm(ModelForm):
|
|
||||||
... class Meta:
|
|
||||||
... model = Book
|
|
||||||
>>> w = Writer.objects.get(name='Mike Royko')
|
|
||||||
>>> form = BookForm({'title': 'I May Be Wrong But I Doubt It', 'author' : w.pk})
|
|
||||||
>>> form.is_valid()
|
|
||||||
True
|
|
||||||
>>> form.save()
|
|
||||||
<Book: Book object>
|
|
||||||
>>> form = BookForm({'title': 'I May Be Wrong But I Doubt It', 'author' : w.pk})
|
|
||||||
>>> form.is_valid()
|
|
||||||
False
|
|
||||||
>>> form._errors
|
|
||||||
{'__all__': [u'Book with this Title and Author already exists.']}
|
|
||||||
>>> form = BookForm({'title': 'I May Be Wrong But I Doubt It'})
|
|
||||||
>>> form.is_valid()
|
|
||||||
True
|
|
||||||
>>> form.save()
|
|
||||||
<Book: Book object>
|
|
||||||
>>> form = BookForm({'title': 'I May Be Wrong But I Doubt It'})
|
|
||||||
>>> form.is_valid()
|
|
||||||
True
|
|
||||||
|
|
||||||
# Test for primary_key being in the form and failing validation.
|
|
||||||
>>> class ExplicitPKForm(ModelForm):
|
|
||||||
... class Meta:
|
|
||||||
... model = ExplicitPK
|
|
||||||
... fields = ('key', 'desc',)
|
|
||||||
>>> form = ExplicitPKForm({'key': u'', 'desc': u'' })
|
|
||||||
>>> form.is_valid()
|
|
||||||
False
|
|
||||||
|
|
||||||
# Ensure keys and blank character strings are tested for uniqueness.
|
|
||||||
>>> form = ExplicitPKForm({'key': u'key1', 'desc': u''})
|
|
||||||
>>> form.is_valid()
|
|
||||||
True
|
|
||||||
>>> form.save()
|
|
||||||
<ExplicitPK: key1>
|
|
||||||
>>> form = ExplicitPKForm({'key': u'key1', 'desc': u''})
|
|
||||||
>>> form.is_valid()
|
|
||||||
False
|
|
||||||
>>> sorted(form.errors.items())
|
|
||||||
[('__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.'])]
|
|
||||||
|
|
||||||
# Choices on CharField and IntegerField
|
# Choices on CharField and IntegerField
|
||||||
>>> class ArticleForm(ModelForm):
|
>>> class ArticleForm(ModelForm):
|
||||||
... class Meta:
|
... class Meta:
|
||||||
|
@ -1605,38 +1540,6 @@ ValidationError: [u'Select a valid choice. z is not one of the available choices
|
||||||
<tr><th><label for="id_description">Description:</label></th><td><input type="text" name="description" id="id_description" /></td></tr>
|
<tr><th><label for="id_description">Description:</label></th><td><input type="text" name="description" id="id_description" /></td></tr>
|
||||||
<tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>
|
<tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>
|
||||||
|
|
||||||
### Validation on unique_for_date
|
|
||||||
|
|
||||||
>>> p = Post.objects.create(title="Django 1.0 is released", slug="Django 1.0", subtitle="Finally", posted=datetime.date(2008, 9, 3))
|
|
||||||
>>> class PostForm(ModelForm):
|
|
||||||
... class Meta:
|
|
||||||
... model = Post
|
|
||||||
|
|
||||||
>>> f = PostForm({'title': "Django 1.0 is released", 'posted': '2008-09-03'})
|
|
||||||
>>> f.is_valid()
|
|
||||||
False
|
|
||||||
>>> f.errors
|
|
||||||
{'title': [u'Title must be unique for Posted date.']}
|
|
||||||
>>> f = PostForm({'title': "Work on Django 1.1 begins", 'posted': '2008-09-03'})
|
|
||||||
>>> f.is_valid()
|
|
||||||
True
|
|
||||||
>>> f = PostForm({'title': "Django 1.0 is released", 'posted': '2008-09-04'})
|
|
||||||
>>> f.is_valid()
|
|
||||||
True
|
|
||||||
>>> f = PostForm({'slug': "Django 1.0", 'posted': '2008-01-01'})
|
|
||||||
>>> f.is_valid()
|
|
||||||
False
|
|
||||||
>>> f.errors
|
|
||||||
{'slug': [u'Slug must be unique for Posted year.']}
|
|
||||||
>>> f = PostForm({'subtitle': "Finally", 'posted': '2008-09-30'})
|
|
||||||
>>> f.is_valid()
|
|
||||||
False
|
|
||||||
>>> f.errors
|
|
||||||
{'subtitle': [u'Subtitle must be unique for Posted month.']}
|
|
||||||
>>> f = PostForm({'subtitle': "Finally", "title": "Django 1.0 is released", "slug": "Django 1.0", 'posted': '2008-09-03'}, instance=p)
|
|
||||||
>>> f.is_valid()
|
|
||||||
True
|
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
>>> import shutil
|
>>> import shutil
|
||||||
>>> shutil.rmtree(temp_storage_dir)
|
>>> shutil.rmtree(temp_storage_dir)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import datetime
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django import forms
|
from django import forms
|
||||||
from models import Category
|
from models import Category, Writer, Book, DerivedBook, Post
|
||||||
|
from mforms import ProductForm, PriceForm, BookForm, DerivedBookForm, ExplicitPKForm, PostForm, DerivedPostForm
|
||||||
|
|
||||||
|
|
||||||
class IncompleteCategoryFormWithFields(forms.ModelForm):
|
class IncompleteCategoryFormWithFields(forms.ModelForm):
|
||||||
|
@ -35,3 +37,140 @@ class ValidationTest(TestCase):
|
||||||
form = IncompleteCategoryFormWithExclude(data={'name': 'some name', 'slug': 'some-slug'})
|
form = IncompleteCategoryFormWithExclude(data={'name': 'some name', 'slug': 'some-slug'})
|
||||||
assert form.is_valid()
|
assert form.is_valid()
|
||||||
|
|
||||||
|
# unique/unique_together validation
|
||||||
|
class UniqueTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.writer = Writer.objects.create(name='Mike Royko')
|
||||||
|
|
||||||
|
def test_simple_unique(self):
|
||||||
|
form = ProductForm({'slug': 'teddy-bear-blue'})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
obj = form.save()
|
||||||
|
form = ProductForm({'slug': 'teddy-bear-blue'})
|
||||||
|
self.assertEqual(len(form.errors), 1)
|
||||||
|
self.assertEqual(form.errors['slug'], [u'Product with this Slug already exists.'])
|
||||||
|
form = ProductForm({'slug': 'teddy-bear-blue'}, instance=obj)
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
|
||||||
|
def test_unique_together(self):
|
||||||
|
"""ModelForm test of unique_together constraint"""
|
||||||
|
form = PriceForm({'price': '6.00', 'quantity': '1'})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form.save()
|
||||||
|
form = PriceForm({'price': '6.00', 'quantity': '1'})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(len(form.errors), 1)
|
||||||
|
self.assertEqual(form.errors['__all__'], [u'Price with this Price and Quantity already exists.'])
|
||||||
|
|
||||||
|
def test_unique_null(self):
|
||||||
|
title = 'I May Be Wrong But I Doubt It'
|
||||||
|
form = BookForm({'title': title, 'author': self.writer.pk})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form.save()
|
||||||
|
form = BookForm({'title': title, 'author': self.writer.pk})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(len(form.errors), 1)
|
||||||
|
self.assertEqual(form.errors['__all__'], [u'Book with this Title and Author already exists.'])
|
||||||
|
form = BookForm({'title': title})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form.save()
|
||||||
|
form = BookForm({'title': title})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
|
||||||
|
def test_inherited_unique(self):
|
||||||
|
title = 'Boss'
|
||||||
|
Book.objects.create(title=title, author=self.writer, special_id=1)
|
||||||
|
form = DerivedBookForm({'title': 'Other', 'author': self.writer.pk, 'special_id': u'1', 'isbn': '12345'})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(len(form.errors), 1)
|
||||||
|
self.assertEqual(form.errors['special_id'], [u'Book with this Special id already exists.'])
|
||||||
|
|
||||||
|
def test_inherited_unique_together(self):
|
||||||
|
title = 'Boss'
|
||||||
|
form = BookForm({'title': title, 'author': self.writer.pk})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form.save()
|
||||||
|
form = DerivedBookForm({'title': title, 'author': self.writer.pk, 'isbn': '12345'})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(len(form.errors), 1)
|
||||||
|
self.assertEqual(form.errors['__all__'], [u'Book with this Title and Author already exists.'])
|
||||||
|
|
||||||
|
def test_abstract_inherited_unique(self):
|
||||||
|
title = 'Boss'
|
||||||
|
isbn = '12345'
|
||||||
|
dbook = DerivedBook.objects.create(title=title, author=self.writer, isbn=isbn)
|
||||||
|
form = DerivedBookForm({'title': 'Other', 'author': self.writer.pk, 'isbn': isbn})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(len(form.errors), 1)
|
||||||
|
self.assertEqual(form.errors['isbn'], [u'Derived book with this Isbn already exists.'])
|
||||||
|
|
||||||
|
def test_abstract_inherited_unique_together(self):
|
||||||
|
title = 'Boss'
|
||||||
|
isbn = '12345'
|
||||||
|
dbook = DerivedBook.objects.create(title=title, author=self.writer, isbn=isbn)
|
||||||
|
form = DerivedBookForm({'title': 'Other', 'author': self.writer.pk, 'isbn': '9876', 'suffix1': u'0', 'suffix2': u'0'})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(len(form.errors), 1)
|
||||||
|
self.assertEqual(form.errors['__all__'], [u'Derived book with this Suffix1 and Suffix2 already exists.'])
|
||||||
|
|
||||||
|
def test_explicitpk_unspecified(self):
|
||||||
|
"""Test for primary_key being in the form and failing validation."""
|
||||||
|
form = ExplicitPKForm({'key': u'', 'desc': u'' })
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
|
||||||
|
def test_explicitpk_unique(self):
|
||||||
|
"""Ensure keys and blank character strings are tested for uniqueness."""
|
||||||
|
form = ExplicitPKForm({'key': u'key1', 'desc': u''})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form.save()
|
||||||
|
form = ExplicitPKForm({'key': u'key1', 'desc': u''})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(len(form.errors), 3)
|
||||||
|
self.assertEqual(form.errors['__all__'], [u'Explicit pk with this Key and Desc already exists.'])
|
||||||
|
self.assertEqual(form.errors['desc'], [u'Explicit pk with this Desc already exists.'])
|
||||||
|
self.assertEqual(form.errors['key'], [u'Explicit pk with this Key already exists.'])
|
||||||
|
|
||||||
|
def test_unique_for_date(self):
|
||||||
|
p = Post.objects.create(title="Django 1.0 is released",
|
||||||
|
slug="Django 1.0", subtitle="Finally", posted=datetime.date(2008, 9, 3))
|
||||||
|
form = PostForm({'title': "Django 1.0 is released", 'posted': '2008-09-03'})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(len(form.errors), 1)
|
||||||
|
self.assertEqual(form.errors['title'], [u'Title must be unique for Posted date.'])
|
||||||
|
form = PostForm({'title': "Work on Django 1.1 begins", 'posted': '2008-09-03'})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form = PostForm({'title': "Django 1.0 is released", 'posted': '2008-09-04'})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form = PostForm({'slug': "Django 1.0", 'posted': '2008-01-01'})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(len(form.errors), 1)
|
||||||
|
self.assertEqual(form.errors['slug'], [u'Slug must be unique for Posted year.'])
|
||||||
|
form = PostForm({'subtitle': "Finally", 'posted': '2008-09-30'})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(form.errors['subtitle'], [u'Subtitle must be unique for Posted month.'])
|
||||||
|
form = PostForm({'subtitle': "Finally", "title": "Django 1.0 is released",
|
||||||
|
"slug": "Django 1.0", 'posted': '2008-09-03'}, instance=p)
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
|
||||||
|
def test_inherited_unique_for_date(self):
|
||||||
|
p = Post.objects.create(title="Django 1.0 is released",
|
||||||
|
slug="Django 1.0", subtitle="Finally", posted=datetime.date(2008, 9, 3))
|
||||||
|
form = DerivedPostForm({'title': "Django 1.0 is released", 'posted': '2008-09-03'})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(len(form.errors), 1)
|
||||||
|
self.assertEqual(form.errors['title'], [u'Title must be unique for Posted date.'])
|
||||||
|
form = DerivedPostForm({'title': "Work on Django 1.1 begins", 'posted': '2008-09-03'})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form = DerivedPostForm({'title': "Django 1.0 is released", 'posted': '2008-09-04'})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form = DerivedPostForm({'slug': "Django 1.0", 'posted': '2008-01-01'})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(len(form.errors), 1)
|
||||||
|
self.assertEqual(form.errors['slug'], [u'Slug must be unique for Posted year.'])
|
||||||
|
form = DerivedPostForm({'subtitle': "Finally", 'posted': '2008-09-30'})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(form.errors['subtitle'], [u'Subtitle must be unique for Posted month.'])
|
||||||
|
form = DerivedPostForm({'subtitle': "Finally", "title": "Django 1.0 is released",
|
||||||
|
"slug": "Django 1.0", 'posted': '2008-09-03'}, instance=p)
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
|
||||||
|
|
|
@ -9,26 +9,34 @@ class GetUniqueCheckTests(unittest.TestCase):
|
||||||
def test_unique_fields_get_collected(self):
|
def test_unique_fields_get_collected(self):
|
||||||
m = UniqueFieldsModel()
|
m = UniqueFieldsModel()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
([('id',), ('unique_charfield',), ('unique_integerfield',)], []),
|
([(UniqueFieldsModel, ('id',)),
|
||||||
|
(UniqueFieldsModel, ('unique_charfield',)),
|
||||||
|
(UniqueFieldsModel, ('unique_integerfield',))],
|
||||||
|
[]),
|
||||||
m._get_unique_checks()
|
m._get_unique_checks()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_unique_together_gets_picked_up_and_converted_to_tuple(self):
|
def test_unique_together_gets_picked_up_and_converted_to_tuple(self):
|
||||||
m = UniqueTogetherModel()
|
m = UniqueTogetherModel()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
([('ifield', 'cfield',),('ifield', 'efield'), ('id',), ], []),
|
([(UniqueTogetherModel, ('ifield', 'cfield',)),
|
||||||
|
(UniqueTogetherModel, ('ifield', 'efield')),
|
||||||
|
(UniqueTogetherModel, ('id',)), ],
|
||||||
|
[]),
|
||||||
m._get_unique_checks()
|
m._get_unique_checks()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_primary_key_is_considered_unique(self):
|
def test_primary_key_is_considered_unique(self):
|
||||||
m = CustomPKModel()
|
m = CustomPKModel()
|
||||||
self.assertEqual(([('my_pk_field',)], []), m._get_unique_checks())
|
self.assertEqual(([(CustomPKModel, ('my_pk_field',))], []), m._get_unique_checks())
|
||||||
|
|
||||||
def test_unique_for_date_gets_picked_up(self):
|
def test_unique_for_date_gets_picked_up(self):
|
||||||
m = UniqueForDateModel()
|
m = UniqueForDateModel()
|
||||||
self.assertEqual((
|
self.assertEqual((
|
||||||
[('id',)],
|
[(UniqueForDateModel, ('id',))],
|
||||||
[('date', 'count', 'start_date'), ('year', 'count', 'end_date'), ('month', 'order', 'end_date')]
|
[(UniqueForDateModel, 'date', 'count', 'start_date'),
|
||||||
|
(UniqueForDateModel, 'year', 'count', 'end_date'),
|
||||||
|
(UniqueForDateModel, 'month', 'order', 'end_date')]
|
||||||
), m._get_unique_checks()
|
), m._get_unique_checks()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue