Skip to content

Commit

Permalink
Fixed #14951 -- Made the unique_for_{date,month,year} model field con…
Browse files Browse the repository at this point in the history
…straints 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
  • Loading branch information
ramiro committed Jan 9, 2011
1 parent 30c8a55 commit 730769d
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 6 deletions.
4 changes: 3 additions & 1 deletion django/db/models/base.py
Expand Up @@ -648,7 +648,7 @@ def _get_unique_checks(self, exclude=None):
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
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.
"""
if exclude is None:
Expand Down Expand Up @@ -740,6 +740,8 @@ def _perform_date_checks(self, date_checks):
# there's a ticket to add a date lookup, we can remove this special
# case if that makes it's way in
date = getattr(self, unique_for)
if date is None:
continue
if lookup_type == 'date':
lookup_kwargs['%s__day' % unique_for] = date.day
lookup_kwargs['%s__month' % unique_for] = date.month
Expand Down
7 changes: 6 additions & 1 deletion tests/modeltests/model_forms/mforms.py
@@ -1,7 +1,8 @@
from django import forms
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 Meta:
Expand Down Expand Up @@ -37,3 +38,7 @@ class CustomWriterForm(ModelForm):

class Meta:
model = Writer

class FlexDatePostForm(ModelForm):
class Meta:
model = FlexibleDatePost
6 changes: 6 additions & 0 deletions tests/modeltests/model_forms/models.py
Expand Up @@ -236,6 +236,12 @@ class CustomFieldForExclusionModel(models.Model):
name = models.CharField(max_length=10)
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': """
>>> from django import forms
>>> from django.forms.models import ModelForm, model_to_dict
Expand Down
20 changes: 17 additions & 3 deletions tests/modeltests/model_forms/tests.py
@@ -1,9 +1,10 @@
import datetime
from django.test import TestCase
from django import forms
from models import Category, Writer, Book, DerivedBook, Post
from mforms import (ProductForm, PriceForm, BookForm, DerivedBookForm,
ExplicitPKForm, PostForm, DerivedPostForm, CustomWriterForm)
from models import Category, Writer, Book, DerivedBook, Post, FlexibleDatePost
from mforms import (ProductForm, PriceForm, BookForm, DerivedBookForm,
ExplicitPKForm, PostForm, DerivedPostForm, CustomWriterForm,
FlexDatePostForm)


class IncompleteCategoryFormWithFields(forms.ModelForm):
Expand Down Expand Up @@ -183,3 +184,16 @@ def test_inherited_unique_for_date(self):
"slug": "Django 1.0", 'posted': '2008-09-03'}, instance=p)
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())
15 changes: 15 additions & 0 deletions tests/modeltests/validation/models.py
Expand Up @@ -63,3 +63,18 @@ class Article(models.Model):
def clean(self):
if self.pub_date is None:
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)
75 changes: 74 additions & 1 deletion tests/modeltests/validation/test_unique.py
@@ -1,12 +1,13 @@
import datetime

from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import connection
from django.test import TestCase
from django.utils import unittest

from models import (CustomPKModel, UniqueTogetherModel, UniqueFieldsModel,
UniqueForDateModel, ModelToValidate)
UniqueForDateModel, ModelToValidate, Post, FlexibleDatePost)


class GetUniqueCheckTests(unittest.TestCase):
Expand Down Expand Up @@ -76,3 +77,75 @@ def test():
mtv = ModelToValidate(number=10, name='Some Name')
mtv.full_clean()
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.")

0 comments on commit 730769d

Please sign in to comment.