Я не могу сказать, является ли появление LEFT OUTER JOIN
проблемой само по себе, но, в любом случае, может быть интересно отметить, в каких случаях эти внешние соединения действительно происходят.
Это наивная попытка проиллюстрировать вышесказанное на некоторых примерах запросов.
Предположим, у нас есть несколько моделей, использующих многотабличное наследование:
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)
По умолчанию дочерние экземпляры получают parent_ptr
, а родительские экземпляры могут обращаться к дочерним объектам (если они существуют) с помощью childone
или childtwo
. Обратите внимание, что parent_ptr
представляет собой взаимно-однозначное отношение, которое используется в качестве первичного ключа (фактические дочерние таблицы не имеют столбца id
).
Вот быстрый и грязный модульный тест с некоторыми наивными примерами Django
запросов, показывающий соответствующее количество вхождений INNER JOIN
и OUTER JOIN
в 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'))
Обратите внимание, что не все эти запросы имеют смысл: они предназначены только для иллюстративных целей.
Также обратите внимание, что этот тестовый код не является СУХИМ, но это сделано специально.
person
djvg
schedule
19.12.2018
What are the alternatives to multi-table inheritance when I need to reference a base class in another model?
Не могли бы вы пояснить, что вы имеете в виду? - person ptr   schedule 08.05.2014