summaryrefslogtreecommitdiff
path: root/tests/select_related_regress
diff options
context:
space:
mode:
authorFlorian Apolloner <florian@apolloner.eu>2013-02-26 09:53:47 +0100
committerFlorian Apolloner <florian@apolloner.eu>2013-02-26 14:36:57 +0100
commit89f40e36246100df6a11316c31a76712ebc6c501 (patch)
tree6e65639683ddaf2027908d1ecb1739e0e2ff853b /tests/select_related_regress
parentb3d2ccb5bfbaf6e7fe1f98843baaa48c35a70950 (diff)
downloaddjango-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__.py0
-rw-r--r--tests/select_related_regress/models.py110
-rw-r--r--tests/select_related_regress/tests.py175
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')