From 04d9730b127c689b8eda01cbc913efa6e2eb230b Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Tue, 5 Feb 2013 23:35:10 -0300 Subject: [PATCH] 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. --- django/contrib/contenttypes/generic.py | 4 +- .../generic_relations_regress/models.py | 31 ++++++++++++++ .../generic_relations_regress/tests.py | 40 ++++++++++++++++++- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index d849d1607e..20ff7042bc 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -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) diff --git a/tests/regressiontests/generic_relations_regress/models.py b/tests/regressiontests/generic_relations_regress/models.py index bdd8ec5eda..2795471f7f 100644 --- a/tests/regressiontests/generic_relations_regress/models.py +++ b/tests/regressiontests/generic_relations_regress/models.py @@ -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) diff --git a/tests/regressiontests/generic_relations_regress/tests.py b/tests/regressiontests/generic_relations_regress/tests.py index 9aa3268fe2..b53129aa12 100644 --- a/tests/regressiontests/generic_relations_regress/tests.py +++ b/tests/regressiontests/generic_relations_regress/tests.py @@ -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()