Whether the occurrence of LEFT OUTER JOIN
is an issue in itself, I cannot say, but, in any case, it may be interesting to note in which cases these outer joins actually occur.
This is a naive attempt to illustrate the above, using some example queries.
Suppose we have some models using multi-table inheritance as follows:
from django.db import models
class Parent(models.Model):
parent_field = models.CharField(max_length=10)
class ChildOne(Parent):
child_one_field = models.CharField(max_length=10)
class ChildTwo(Parent):
child_two_field = models.CharField(max_length=10)
By default, the child instances get a parent_ptr
and parent instances can access child objects (if they exist) using childone
or childtwo
. Note that parent_ptr
represents a one-to-one relation which is used as the primary key (the actual child tables have no id
column).
Here's a quick-and-dirty unit test with some naive Django
query examples, showing the corresponding number of occurrences of INNER JOIN
and OUTER JOIN
in the SQL
:
import re
from django.test import TestCase
from inheritance.models import (Parent, ChildOne, ChildTwo)
def count_joins(query, inner_outer):
""" Count the occurrences of JOIN in the query """
return len(re.findall('{} join'.format(inner_outer), str(query).lower()))
class TestMultiTableInheritance(TestCase):
def test_queries(self):
# get children (with parent info)
query = ChildOne.objects.all().query
self.assertEqual(1, count_joins(query, 'inner'))
self.assertEqual(0, count_joins(query, 'outer'))
# get parents
query = Parent.objects.all().query
self.assertEqual(0, count_joins(query, 'inner'))
self.assertEqual(0, count_joins(query, 'outer'))
# filter children by parent field
query = ChildOne.objects.filter(parent_field=parent_value).query
self.assertEqual(1, count_joins(query, 'inner'))
self.assertEqual(0, count_joins(query, 'outer'))
# filter parents by child field
query = Parent.objects.filter(childone__child_one_field=child_value).query
self.assertEqual(1, count_joins(query, 'inner'))
self.assertEqual(0, count_joins(query, 'outer'))
# get child field values via parent
query = Parent.objects.values_list('childone__child_one_field').query
self.assertEqual(0, count_joins(query, 'inner'))
self.assertEqual(1, count_joins(query, 'outer'))
# get multiple child field values via parent
query = Parent.objects.values_list('childone__child_one_field',
'childtwo__child_two_field').query
self.assertEqual(0, count_joins(query, 'inner'))
self.assertEqual(2, count_joins(query, 'outer'))
# get child-two field value from child-one, through parent
query = ChildOne.objects.values_list('parent_ptr__childtwo__child_two_field').query
self.assertEqual(1, count_joins(query, 'inner'))
self.assertEqual(1, count_joins(query, 'outer'))
# get parent field value from parent, but through child
query = Parent.objects.values_list('childone__parent_field').query
self.assertEqual(0, count_joins(query, 'inner'))
self.assertEqual(2, count_joins(query, 'outer'))
# filter parents by parent field, but through child
query = Parent.objects.filter(childone__parent_field=parent_value).query
self.assertEqual(2, count_joins(query, 'inner'))
self.assertEqual(0, count_joins(query, 'outer'))
Note, not all of these queries make sense: they are just for illustrative purposes.
Also note that this test code is not DRY, but that is on purpose.
What are the alternatives to multi-table inheritance when I need to reference a base class in another model?
Could you clarify what you mean by this? – Fulvi