mirror of https://github.com/django/django.git
Fixed #14951 -- Made the unique_for_{date,month,year} model field constraints to not fail when the related DateField is empty.
Existing modelforms tests were extended to cover this case and an equivalent set of tests was added for the model functionality. git-svn-id: http://code.djangoproject.com/svn/django/trunk@15167 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
30c8a5574a
commit
730769d1af
|
@ -648,7 +648,7 @@ class Model(object):
|
||||||
called from a ModelForm, some fields may have been excluded; we can't
|
called from a ModelForm, some fields may have been excluded; we can't
|
||||||
perform a unique check on a model that is missing fields involved
|
perform a unique check on a model that is missing fields involved
|
||||||
in that check.
|
in that check.
|
||||||
Fields that did not validate should also be exluded, but they need
|
Fields that did not validate should also be excluded, but they need
|
||||||
to be passed in via the exclude argument.
|
to be passed in via the exclude argument.
|
||||||
"""
|
"""
|
||||||
if exclude is None:
|
if exclude is None:
|
||||||
|
@ -740,6 +740,8 @@ class Model(object):
|
||||||
# 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
|
||||||
date = getattr(self, unique_for)
|
date = getattr(self, unique_for)
|
||||||
|
if date is None:
|
||||||
|
continue
|
||||||
if lookup_type == 'date':
|
if lookup_type == 'date':
|
||||||
lookup_kwargs['%s__day' % unique_for] = date.day
|
lookup_kwargs['%s__day' % unique_for] = date.day
|
||||||
lookup_kwargs['%s__month' % unique_for] = date.month
|
lookup_kwargs['%s__month' % unique_for] = date.month
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms import ModelForm
|
from django.forms import ModelForm
|
||||||
|
|
||||||
from models import Product, Price, Book, DerivedBook, ExplicitPK, Post, DerivedPost, Writer
|
from models import (Product, Price, Book, DerivedBook, ExplicitPK, Post,
|
||||||
|
DerivedPost, Writer, FlexibleDatePost)
|
||||||
|
|
||||||
class ProductForm(ModelForm):
|
class ProductForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -37,3 +38,7 @@ class CustomWriterForm(ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Writer
|
model = Writer
|
||||||
|
|
||||||
|
class FlexDatePostForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = FlexibleDatePost
|
||||||
|
|
|
@ -236,6 +236,12 @@ class CustomFieldForExclusionModel(models.Model):
|
||||||
name = models.CharField(max_length=10)
|
name = models.CharField(max_length=10)
|
||||||
markup = MarkupField()
|
markup = MarkupField()
|
||||||
|
|
||||||
|
class FlexibleDatePost(models.Model):
|
||||||
|
title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
|
||||||
|
slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
|
||||||
|
subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
|
||||||
|
posted = models.DateField(blank=True, null=True)
|
||||||
|
|
||||||
__test__ = {'API_TESTS': """
|
__test__ = {'API_TESTS': """
|
||||||
>>> from django import forms
|
>>> from django import forms
|
||||||
>>> from django.forms.models import ModelForm, model_to_dict
|
>>> from django.forms.models import ModelForm, model_to_dict
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import datetime
|
import datetime
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django import forms
|
from django import forms
|
||||||
from models import Category, Writer, Book, DerivedBook, Post
|
from models import Category, Writer, Book, DerivedBook, Post, FlexibleDatePost
|
||||||
from mforms import (ProductForm, PriceForm, BookForm, DerivedBookForm,
|
from mforms import (ProductForm, PriceForm, BookForm, DerivedBookForm,
|
||||||
ExplicitPKForm, PostForm, DerivedPostForm, CustomWriterForm)
|
ExplicitPKForm, PostForm, DerivedPostForm, CustomWriterForm,
|
||||||
|
FlexDatePostForm)
|
||||||
|
|
||||||
|
|
||||||
class IncompleteCategoryFormWithFields(forms.ModelForm):
|
class IncompleteCategoryFormWithFields(forms.ModelForm):
|
||||||
|
@ -183,3 +184,16 @@ class UniqueTest(TestCase):
|
||||||
"slug": "Django 1.0", 'posted': '2008-09-03'}, instance=p)
|
"slug": "Django 1.0", 'posted': '2008-09-03'}, instance=p)
|
||||||
self.assertTrue(form.is_valid())
|
self.assertTrue(form.is_valid())
|
||||||
|
|
||||||
|
def test_unique_for_date_with_nullable_date(self):
|
||||||
|
p = FlexibleDatePost.objects.create(title="Django 1.0 is released",
|
||||||
|
slug="Django 1.0", subtitle="Finally", posted=datetime.date(2008, 9, 3))
|
||||||
|
|
||||||
|
form = FlexDatePostForm({'title': "Django 1.0 is released"})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form = FlexDatePostForm({'slug': "Django 1.0"})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form = FlexDatePostForm({'subtitle': "Finally"})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
form = FlexDatePostForm({'subtitle': "Finally", "title": "Django 1.0 is released",
|
||||||
|
"slug": "Django 1.0"}, instance=p)
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
|
|
@ -63,3 +63,18 @@ class Article(models.Model):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.pub_date is None:
|
if self.pub_date is None:
|
||||||
self.pub_date = datetime.now()
|
self.pub_date = datetime.now()
|
||||||
|
|
||||||
|
class Post(models.Model):
|
||||||
|
title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
|
||||||
|
slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
|
||||||
|
subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
|
||||||
|
posted = models.DateField()
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class FlexibleDatePost(models.Model):
|
||||||
|
title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
|
||||||
|
slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
|
||||||
|
subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
|
||||||
|
posted = models.DateField(blank=True, null=True)
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
|
|
||||||
from models import (CustomPKModel, UniqueTogetherModel, UniqueFieldsModel,
|
from models import (CustomPKModel, UniqueTogetherModel, UniqueFieldsModel,
|
||||||
UniqueForDateModel, ModelToValidate)
|
UniqueForDateModel, ModelToValidate, Post, FlexibleDatePost)
|
||||||
|
|
||||||
|
|
||||||
class GetUniqueCheckTests(unittest.TestCase):
|
class GetUniqueCheckTests(unittest.TestCase):
|
||||||
|
@ -76,3 +77,75 @@ class PerformUniqueChecksTest(TestCase):
|
||||||
mtv = ModelToValidate(number=10, name='Some Name')
|
mtv = ModelToValidate(number=10, name='Some Name')
|
||||||
mtv.full_clean()
|
mtv.full_clean()
|
||||||
self.assertNumQueries(0, test)
|
self.assertNumQueries(0, test)
|
||||||
|
|
||||||
|
def test_unique_for_date(self):
|
||||||
|
p1 = Post.objects.create(title="Django 1.0 is released",
|
||||||
|
slug="Django 1.0", subtitle="Finally", posted=datetime.date(2008, 9, 3))
|
||||||
|
|
||||||
|
p = Post(title="Django 1.0 is released", posted=datetime.date(2008, 9, 3))
|
||||||
|
try:
|
||||||
|
p.full_clean()
|
||||||
|
except ValidationError, e:
|
||||||
|
self.assertEqual(e.message_dict, {'title': [u'Title must be unique for Posted date.']})
|
||||||
|
else:
|
||||||
|
self.fail('unique_for_date checks should catch this.')
|
||||||
|
|
||||||
|
# Should work without errors
|
||||||
|
p = Post(title="Work on Django 1.1 begins", posted=datetime.date(2008, 9, 3))
|
||||||
|
p.full_clean()
|
||||||
|
|
||||||
|
# Should work without errors
|
||||||
|
p = Post(title="Django 1.0 is released", posted=datetime.datetime(2008, 9,4))
|
||||||
|
p.full_clean()
|
||||||
|
|
||||||
|
p = Post(slug="Django 1.0", posted=datetime.datetime(2008, 1, 1))
|
||||||
|
try:
|
||||||
|
p.full_clean()
|
||||||
|
except ValidationError, e:
|
||||||
|
self.assertEqual(e.message_dict, {'slug': [u'Slug must be unique for Posted year.']})
|
||||||
|
else:
|
||||||
|
self.fail('unique_for_year checks should catch this.')
|
||||||
|
|
||||||
|
p = Post(subtitle="Finally", posted=datetime.datetime(2008, 9, 30))
|
||||||
|
try:
|
||||||
|
p.full_clean()
|
||||||
|
except ValidationError, e:
|
||||||
|
self.assertEqual(e.message_dict, {'subtitle': [u'Subtitle must be unique for Posted month.']})
|
||||||
|
else:
|
||||||
|
self.fail('unique_for_month checks should catch this.')
|
||||||
|
|
||||||
|
p = Post(title="Django 1.0 is released")
|
||||||
|
try:
|
||||||
|
p.full_clean()
|
||||||
|
except ValidationError, e:
|
||||||
|
self.assertEqual(e.message_dict, {'posted': [u'This field cannot be null.']})
|
||||||
|
else:
|
||||||
|
self.fail("Model validation shouldn't allow an absent value for a DateField without null=True.")
|
||||||
|
|
||||||
|
def test_unique_for_date_with_nullable_date(self):
|
||||||
|
p1 = FlexibleDatePost.objects.create(title="Django 1.0 is released",
|
||||||
|
slug="Django 1.0", subtitle="Finally", posted=datetime.date(2008, 9, 3))
|
||||||
|
|
||||||
|
p = FlexibleDatePost(title="Django 1.0 is released")
|
||||||
|
try:
|
||||||
|
p.full_clean()
|
||||||
|
except ValidationError, e:
|
||||||
|
self.fail("unique_for_date checks shouldn't trigger when the associated DateField is None.")
|
||||||
|
except:
|
||||||
|
self.fail("unique_for_date checks shouldn't explode when the associated DateField is None.")
|
||||||
|
|
||||||
|
p = FlexibleDatePost(slug="Django 1.0")
|
||||||
|
try:
|
||||||
|
p.full_clean()
|
||||||
|
except ValidationError, e:
|
||||||
|
self.fail("unique_for_year checks shouldn't trigger when the associated DateField is None.")
|
||||||
|
except:
|
||||||
|
self.fail("unique_for_year checks shouldn't explode when the associated DateField is None.")
|
||||||
|
|
||||||
|
p = FlexibleDatePost(subtitle="Finally")
|
||||||
|
try:
|
||||||
|
p.full_clean()
|
||||||
|
except ValidationError, e:
|
||||||
|
self.fail("unique_for_month checks shouldn't trigger when the associated DateField is None.")
|
||||||
|
except:
|
||||||
|
self.fail("unique_for_month checks shouldn't explode when the associated DateField is None.")
|
||||||
|
|
Loading…
Reference in New Issue