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/select_related_regress | |
parent | b3d2ccb5bfbaf6e7fe1f98843baaa48c35a70950 (diff) | |
download | django-89f40e36246100df6a11316c31a76712ebc6c501.tar.gz |
Merged regressiontests and modeltests into the test root.
Diffstat (limited to 'tests/select_related_regress')
-rw-r--r-- | tests/select_related_regress/__init__.py | 0 | ||||
-rw-r--r-- | tests/select_related_regress/models.py | 110 | ||||
-rw-r--r-- | tests/select_related_regress/tests.py | 175 |
3 files changed, 285 insertions, 0 deletions
diff --git a/tests/select_related_regress/__init__.py b/tests/select_related_regress/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/select_related_regress/__init__.py diff --git a/tests/select_related_regress/models.py b/tests/select_related_regress/models.py new file mode 100644 index 0000000000..620b8eb5ea --- /dev/null +++ b/tests/select_related_regress/models.py @@ -0,0 +1,110 @@ +from __future__ import unicode_literals + +from django.db import models +from django.utils.encoding import python_2_unicode_compatible + + +@python_2_unicode_compatible +class Building(models.Model): + name = models.CharField(max_length=10) + + def __str__(self): + return "Building: %s" % self.name + +@python_2_unicode_compatible +class Device(models.Model): + building = models.ForeignKey('Building') + name = models.CharField(max_length=10) + + def __str__(self): + return "device '%s' in building %s" % (self.name, self.building) + +@python_2_unicode_compatible +class Port(models.Model): + device = models.ForeignKey('Device') + port_number = models.CharField(max_length=10) + + def __str__(self): + return "%s/%s" % (self.device.name, self.port_number) + +@python_2_unicode_compatible +class Connection(models.Model): + start = models.ForeignKey(Port, related_name='connection_start', + unique=True) + end = models.ForeignKey(Port, related_name='connection_end', unique=True) + + def __str__(self): + return "%s to %s" % (self.start, self.end) + +# Another non-tree hierarchy that exercises code paths similar to the above +# example, but in a slightly different configuration. +class TUser(models.Model): + name = models.CharField(max_length=200) + +class Person(models.Model): + user = models.ForeignKey(TUser, unique=True) + +class Organizer(models.Model): + person = models.ForeignKey(Person) + +class Student(models.Model): + person = models.ForeignKey(Person) + +class Class(models.Model): + org = models.ForeignKey(Organizer) + +class Enrollment(models.Model): + std = models.ForeignKey(Student) + cls = models.ForeignKey(Class) + +# Models for testing bug #8036. +class Country(models.Model): + name = models.CharField(max_length=50) + +class State(models.Model): + name = models.CharField(max_length=50) + country = models.ForeignKey(Country) + +class ClientStatus(models.Model): + name = models.CharField(max_length=50) + +class Client(models.Model): + name = models.CharField(max_length=50) + state = models.ForeignKey(State, null=True) + status = models.ForeignKey(ClientStatus) + +class SpecialClient(Client): + value = models.IntegerField() + +# Some model inheritance exercises +@python_2_unicode_compatible +class Parent(models.Model): + name = models.CharField(max_length=10) + + def __str__(self): + return self.name + +class Child(Parent): + value = models.IntegerField() + +@python_2_unicode_compatible +class Item(models.Model): + name = models.CharField(max_length=10) + child = models.ForeignKey(Child, null=True) + + def __str__(self): + return self.name + +# Models for testing bug #19870. +@python_2_unicode_compatible +class Fowl(models.Model): + name = models.CharField(max_length=10) + + def __str__(self): + return self.name + +class Hen(Fowl): + pass + +class Chick(Fowl): + mother = models.ForeignKey(Hen) diff --git a/tests/select_related_regress/tests.py b/tests/select_related_regress/tests.py new file mode 100644 index 0000000000..f6d21b2dd9 --- /dev/null +++ b/tests/select_related_regress/tests.py @@ -0,0 +1,175 @@ +from __future__ import absolute_import, unicode_literals + +from django.test import TestCase +from django.utils import six + +from .models import (Building, Child, Device, Port, Item, Country, Connection, + ClientStatus, State, Client, SpecialClient, TUser, Person, Student, + Organizer, Class, Enrollment, Hen, Chick) + + +class SelectRelatedRegressTests(TestCase): + + def test_regression_7110(self): + """ + Regression test for bug #7110. + + When using select_related(), we must query the + Device and Building tables using two different aliases (each) in order to + differentiate the start and end Connection fields. The net result is that + both the "connections = ..." queries here should give the same results + without pulling in more than the absolute minimum number of tables + (history has shown that it's easy to make a mistake in the implementation + and include some unnecessary bonus joins). + """ + + b=Building.objects.create(name='101') + dev1=Device.objects.create(name="router", building=b) + dev2=Device.objects.create(name="switch", building=b) + dev3=Device.objects.create(name="server", building=b) + port1=Port.objects.create(port_number='4',device=dev1) + port2=Port.objects.create(port_number='7',device=dev2) + port3=Port.objects.create(port_number='1',device=dev3) + c1=Connection.objects.create(start=port1, end=port2) + c2=Connection.objects.create(start=port2, end=port3) + + connections=Connection.objects.filter(start__device__building=b, end__device__building=b).order_by('id') + self.assertEqual([(c.id, six.text_type(c.start), six.text_type(c.end)) for c in connections], + [(c1.id, 'router/4', 'switch/7'), (c2.id, 'switch/7', 'server/1')]) + + connections=Connection.objects.filter(start__device__building=b, end__device__building=b).select_related().order_by('id') + self.assertEqual([(c.id, six.text_type(c.start), six.text_type(c.end)) for c in connections], + [(c1.id, 'router/4', 'switch/7'), (c2.id, 'switch/7', 'server/1')]) + + # This final query should only have seven tables (port, device and building + # twice each, plus connection once). Thus, 6 joins plus the FROM table. + self.assertEqual(str(connections.query).count(" JOIN "), 6) + + + def test_regression_8106(self): + """ + Regression test for bug #8106. + + Same sort of problem as the previous test, but this time there are + more extra tables to pull in as part of the select_related() and some + of them could potentially clash (so need to be kept separate). + """ + + us = TUser.objects.create(name="std") + usp = Person.objects.create(user=us) + uo = TUser.objects.create(name="org") + uop = Person.objects.create(user=uo) + s = Student.objects.create(person = usp) + o = Organizer.objects.create(person = uop) + c = Class.objects.create(org=o) + e = Enrollment.objects.create(std=s, cls=c) + + e_related = Enrollment.objects.all().select_related()[0] + self.assertEqual(e_related.std.person.user.name, "std") + self.assertEqual(e_related.cls.org.person.user.name, "org") + + def test_regression_8036(self): + """ + Regression test for bug #8036 + + the first related model in the tests below + ("state") is empty and we try to select the more remotely related + state__country. The regression here was not skipping the empty column results + for country before getting status. + """ + + australia = Country.objects.create(name='Australia') + active = ClientStatus.objects.create(name='active') + client = Client.objects.create(name='client', status=active) + + self.assertEqual(client.status, active) + self.assertEqual(Client.objects.select_related()[0].status, active) + self.assertEqual(Client.objects.select_related('state')[0].status, active) + self.assertEqual(Client.objects.select_related('state', 'status')[0].status, active) + self.assertEqual(Client.objects.select_related('state__country')[0].status, active) + self.assertEqual(Client.objects.select_related('state__country', 'status')[0].status, active) + self.assertEqual(Client.objects.select_related('status')[0].status, active) + + def test_multi_table_inheritance(self): + """ Exercising select_related() with multi-table model inheritance. """ + c1 = Child.objects.create(name="child1", value=42) + i1 = Item.objects.create(name="item1", child=c1) + i2 = Item.objects.create(name="item2") + + self.assertQuerysetEqual( + Item.objects.select_related("child").order_by("name"), + ["<Item: item1>", "<Item: item2>"] + ) + + def test_regression_12851(self): + """ + Regression for #12851 + + Deferred fields are used correctly if you select_related a subset + of fields. + """ + australia = Country.objects.create(name='Australia') + active = ClientStatus.objects.create(name='active') + + wa = State.objects.create(name="Western Australia", country=australia) + c1 = Client.objects.create(name='Brian Burke', state=wa, status=active) + burke = Client.objects.select_related('state').defer('state__name').get(name='Brian Burke') + + self.assertEqual(burke.name, 'Brian Burke') + self.assertEqual(burke.state.name, 'Western Australia') + + # Still works if we're dealing with an inherited class + sc1 = SpecialClient.objects.create(name='Troy Buswell', state=wa, status=active, value=42) + troy = SpecialClient.objects.select_related('state').defer('state__name').get(name='Troy Buswell') + + self.assertEqual(troy.name, 'Troy Buswell') + self.assertEqual(troy.value, 42) + self.assertEqual(troy.state.name, 'Western Australia') + + # Still works if we defer an attribute on the inherited class + troy = SpecialClient.objects.select_related('state').defer('value', 'state__name').get(name='Troy Buswell') + + self.assertEqual(troy.name, 'Troy Buswell') + self.assertEqual(troy.value, 42) + self.assertEqual(troy.state.name, 'Western Australia') + + # Also works if you use only, rather than defer + troy = SpecialClient.objects.select_related('state').only('name', 'state').get(name='Troy Buswell') + + self.assertEqual(troy.name, 'Troy Buswell') + self.assertEqual(troy.value, 42) + self.assertEqual(troy.state.name, 'Western Australia') + + def test_null_join_promotion(self): + australia = Country.objects.create(name='Australia') + active = ClientStatus.objects.create(name='active') + + wa = State.objects.create(name="Western Australia", country=australia) + bob = Client.objects.create(name='Bob', status=active) + jack = Client.objects.create(name='Jack', status=active, state=wa) + qs = Client.objects.filter(state=wa).select_related('state') + with self.assertNumQueries(1): + self.assertEqual(list(qs), [jack]) + self.assertEqual(qs[0].state, wa) + # The select_related join wasn't promoted as there was already an + # existing (even if trimmed) inner join to state. + self.assertFalse('LEFT OUTER' in str(qs.query)) + qs = Client.objects.select_related('state').order_by('name') + with self.assertNumQueries(1): + self.assertEqual(list(qs), [bob, jack]) + self.assertIs(qs[0].state, None) + self.assertEqual(qs[1].state, wa) + # The select_related join was promoted as there is already an + # existing join. + self.assertTrue('LEFT OUTER' in str(qs.query)) + + def test_regression_19870(self): + """ + Regression for #19870 + + """ + hen = Hen.objects.create(name='Hen') + chick = Chick.objects.create(name='Chick', mother=hen) + + self.assertEqual(Chick.objects.all()[0].mother.name, 'Hen') + self.assertEqual(Chick.objects.select_related()[0].mother.name, 'Hen') |