Fixed #13085 -- Don't fail on creation of model with GFK to a model with __len__() returning zero.

Also, according to the comments on the ticket and its duplicates, added
tests execising saving an instance of a model with a GFK to:

* An unsaved object -- This actually doesn't generate the same failure
  but another ORM-level exception. The test verifies it's the case.

* An instance of a model with a __nonzero__() method thant returns False
  for it. This doesn't fail because that code path isn't executed.

* An instance of a model with a CharField PK and an empty value for it.
  This doesn't fail.
This commit is contained in:
Ramiro Morales 2013-02-05 23:35:10 -03:00
parent 2b916895a1
commit 04d9730b12
3 changed files with 72 additions and 3 deletions

View File

@ -43,7 +43,7 @@ class GenericForeignKey(object):
def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs):
"""
Handles initializing an object with the generic FK instaed of
Handles initializing an object with the generic FK instead of
content-type/object-id fields.
"""
if self.name in kwargs:
@ -52,7 +52,7 @@ class GenericForeignKey(object):
kwargs[self.fk_field] = value._get_pk_val()
def get_content_type(self, obj=None, id=None, using=None):
if obj:
if obj is not None:
return ContentType.objects.db_manager(obj._state.db).get_for_model(obj)
elif id:
return ContentType.objects.db_manager(using).get_for_id(id)

View File

@ -91,3 +91,34 @@ class Company(models.Model):
def __str__(self):
return "Company: %s" % self.name
# For testing #13085 fix, we also use Note model defined above
class Developer(models.Model):
name = models.CharField(max_length=15)
@python_2_unicode_compatible
class Team(models.Model):
name = models.CharField(max_length=15)
members = models.ManyToManyField(Developer)
def __str__(self):
return "%s team" % self.name
def __len__(self):
return self.members.count()
class Guild(models.Model):
name = models.CharField(max_length=15)
members = models.ManyToManyField(Developer)
def __nonzero__(self):
return self.members.count()
class Tag(models.Model):
content_type = models.ForeignKey(ContentType, related_name='g_r_r_tags')
object_id = models.CharField(max_length=15)
content_object = generic.GenericForeignKey()
label = models.CharField(max_length=15)
class Board(models.Model):
name = models.CharField(primary_key=True, max_length=15)

View File

@ -1,8 +1,10 @@
from django.db.models import Q
from django.db.utils import IntegrityError
from django.test import TestCase
from .models import (Address, Place, Restaurant, Link, CharLink, TextLink,
Person, Contact, Note, Organization, OddRelation1, OddRelation2, Company)
Person, Contact, Note, Organization, OddRelation1, OddRelation2, Company,
Developer, Team, Guild, Tag, Board)
class GenericRelationTests(TestCase):
@ -98,3 +100,39 @@ class GenericRelationTests(TestCase):
self.assertEqual(len(places), 2)
self.assertEqual(count_places(p1), 1)
self.assertEqual(count_places(p2), 1)
def test_target_model_is_unsaved(self):
"""Test related to #13085"""
# Fails with another, ORM-level error
dev1 = Developer(name='Joe')
note = Note(note='Deserves promotion', content_object=dev1)
self.assertRaisesMessage(IntegrityError,
"generic_relations_regress_note.object_id may not be NULL",
note.save)
def test_target_model_len_zero(self):
"""Test for #13085 -- __len__() returns 0"""
team1 = Team.objects.create(name='Backend devs')
try:
note = Note(note='Deserve a bonus', content_object=team1)
except Exception as e:
if issubclass(type(e), Exception) and str(e) == 'Impossible arguments to GFK.get_content_type!':
self.fail("Saving model with GenericForeignKey to model instance whose __len__ method returns 0 shouldn't fail.")
raise e
note.save()
def test_target_model_nonzero_false(self):
"""Test related to #13085"""
# __nonzero__() returns False -- This actually doesn't currently fail.
# This test validates that
g1 = Guild.objects.create(name='First guild')
note = Note(note='Note for guild', content_object=g1)
note.save()
def test_gfk_to_model_with_empty_pk(self):
"""Test related to #13085"""
# Saving model with GenericForeignKey to model instance with an
# empty CharField PK
b1 = Board.objects.create(name='')
tag = Tag(label='VP', content_object=b1)
tag.save()