diff options
| author | Florian Apolloner <florian@apolloner.eu> | 2013-02-26 09:53:47 +0100 |
|---|---|---|
| committer | Florian Apolloner <florian@apolloner.eu> | 2013-02-26 14:36:57 +0100 |
| commit | 89f40e36246100df6a11316c31a76712ebc6c501 (patch) | |
| tree | 6e65639683ddaf2027908d1ecb1739e0e2ff853b /tests/generic_relations_regress | |
| parent | b3d2ccb5bfbaf6e7fe1f98843baaa48c35a70950 (diff) | |
Merged regressiontests and modeltests into the test root.
Diffstat (limited to 'tests/generic_relations_regress')
| -rw-r--r-- | tests/generic_relations_regress/__init__.py | 0 | ||||
| -rw-r--r-- | tests/generic_relations_regress/models.py | 124 | ||||
| -rw-r--r-- | tests/generic_relations_regress/tests.py | 137 |
3 files changed, 261 insertions, 0 deletions
diff --git a/tests/generic_relations_regress/__init__.py b/tests/generic_relations_regress/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/generic_relations_regress/__init__.py diff --git a/tests/generic_relations_regress/models.py b/tests/generic_relations_regress/models.py new file mode 100644 index 0000000000..2795471f7f --- /dev/null +++ b/tests/generic_relations_regress/models.py @@ -0,0 +1,124 @@ +from django.contrib.contenttypes import generic +from django.contrib.contenttypes.models import ContentType +from django.db import models +from django.utils.encoding import python_2_unicode_compatible + + +__all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address', + 'CharLink', 'TextLink', 'OddRelation1', 'OddRelation2', + 'Contact', 'Organization', 'Note', 'Company') + +@python_2_unicode_compatible +class Link(models.Model): + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey() + + def __str__(self): + return "Link to %s id=%s" % (self.content_type, self.object_id) + +@python_2_unicode_compatible +class Place(models.Model): + name = models.CharField(max_length=100) + links = generic.GenericRelation(Link) + + def __str__(self): + return "Place: %s" % self.name + +@python_2_unicode_compatible +class Restaurant(Place): + def __str__(self): + return "Restaurant: %s" % self.name + +@python_2_unicode_compatible +class Address(models.Model): + street = models.CharField(max_length=80) + city = models.CharField(max_length=50) + state = models.CharField(max_length=2) + zipcode = models.CharField(max_length=5) + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey() + + def __str__(self): + return '%s %s, %s %s' % (self.street, self.city, self.state, self.zipcode) + +@python_2_unicode_compatible +class Person(models.Model): + account = models.IntegerField(primary_key=True) + name = models.CharField(max_length=128) + addresses = generic.GenericRelation(Address) + + def __str__(self): + return self.name + +class CharLink(models.Model): + content_type = models.ForeignKey(ContentType) + object_id = models.CharField(max_length=100) + content_object = generic.GenericForeignKey() + +class TextLink(models.Model): + content_type = models.ForeignKey(ContentType) + object_id = models.TextField() + content_object = generic.GenericForeignKey() + +class OddRelation1(models.Model): + name = models.CharField(max_length=100) + clinks = generic.GenericRelation(CharLink) + +class OddRelation2(models.Model): + name = models.CharField(max_length=100) + tlinks = generic.GenericRelation(TextLink) + +# models for test_q_object_or: +class Note(models.Model): + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey() + note = models.TextField() + +class Contact(models.Model): + notes = generic.GenericRelation(Note) + +class Organization(models.Model): + name = models.CharField(max_length=255) + contacts = models.ManyToManyField(Contact, related_name='organizations') + +@python_2_unicode_compatible +class Company(models.Model): + name = models.CharField(max_length=100) + links = generic.GenericRelation(Link) + + 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/generic_relations_regress/tests.py b/tests/generic_relations_regress/tests.py new file mode 100644 index 0000000000..474113584a --- /dev/null +++ b/tests/generic_relations_regress/tests.py @@ -0,0 +1,137 @@ +from django.db.models import Q +from django.db.utils import IntegrityError +from django.test import TestCase, skipIfDBFeature + +from .models import (Address, Place, Restaurant, Link, CharLink, TextLink, + Person, Contact, Note, Organization, OddRelation1, OddRelation2, Company, + Developer, Team, Guild, Tag, Board) + + +class GenericRelationTests(TestCase): + + def test_inherited_models_content_type(self): + """ + Test that GenericRelations on inherited classes use the correct content + type. + """ + + p = Place.objects.create(name="South Park") + r = Restaurant.objects.create(name="Chubby's") + l1 = Link.objects.create(content_object=p) + l2 = Link.objects.create(content_object=r) + self.assertEqual(list(p.links.all()), [l1]) + self.assertEqual(list(r.links.all()), [l2]) + + def test_reverse_relation_pk(self): + """ + Test that the correct column name is used for the primary key on the + originating model of a query. See #12664. + """ + p = Person.objects.create(account=23, name='Chef') + a = Address.objects.create(street='123 Anywhere Place', + city='Conifer', state='CO', + zipcode='80433', content_object=p) + + qs = Person.objects.filter(addresses__zipcode='80433') + self.assertEqual(1, qs.count()) + self.assertEqual('Chef', qs[0].name) + + def test_charlink_delete(self): + oddrel = OddRelation1.objects.create(name='clink') + cl = CharLink.objects.create(content_object=oddrel) + oddrel.delete() + + def test_textlink_delete(self): + oddrel = OddRelation2.objects.create(name='tlink') + tl = TextLink.objects.create(content_object=oddrel) + oddrel.delete() + + def test_q_object_or(self): + """ + Tests that SQL query parameters for generic relations are properly + grouped when OR is used. + + Test for bug http://code.djangoproject.com/ticket/11535 + + In this bug the first query (below) works while the second, with the + query parameters the same but in reverse order, does not. + + The issue is that the generic relation conditions do not get properly + grouped in parentheses. + """ + note_contact = Contact.objects.create() + org_contact = Contact.objects.create() + note = Note.objects.create(note='note', content_object=note_contact) + org = Organization.objects.create(name='org name') + org.contacts.add(org_contact) + # search with a non-matching note and a matching org name + qs = Contact.objects.filter(Q(notes__note__icontains=r'other note') | + Q(organizations__name__icontains=r'org name')) + self.assertTrue(org_contact in qs) + # search again, with the same query parameters, in reverse order + qs = Contact.objects.filter( + Q(organizations__name__icontains=r'org name') | + Q(notes__note__icontains=r'other note')) + self.assertTrue(org_contact in qs) + + def test_join_reuse(self): + qs = Person.objects.filter( + addresses__street='foo' + ).filter( + addresses__street='bar' + ) + self.assertEqual(str(qs.query).count('JOIN'), 2) + + def test_generic_relation_ordering(self): + """ + Test that ordering over a generic relation does not include extraneous + duplicate results, nor excludes rows not participating in the relation. + """ + p1 = Place.objects.create(name="South Park") + p2 = Place.objects.create(name="The City") + c = Company.objects.create(name="Chubby's Intl.") + l1 = Link.objects.create(content_object=p1) + l2 = Link.objects.create(content_object=c) + + places = list(Place.objects.order_by('links__id')) + def count_places(place): + return len([p for p in places if p.id == place.id]) + + 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.assertRaises(IntegrityError, 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() + + @skipIfDBFeature('interprets_empty_strings_as_nulls') + 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() |
