From 2a2708e1b2a0e02fc4fdd4b050d60fb0033dfdde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Fri, 15 Mar 2013 10:55:12 +0200 Subject: [PATCH] Fixed #17502 -- Made joining in inheritance cases consistent The original problem was that when filtering two levels up in inheritance chain, Django optimized the join generation so that the middle model was skipped. But then Django generated joins from top to middle to bottom for SELECT clause, and thus there was one extra join (top->middle->bottom + top -> bottom). This case is fixed in master as the filtering optimization is gone. This has the side effect that in some cases there is still extra join if the SELECT clause doesn't contain anything from middle or bottom. --- tests/model_inheritance_regress/tests.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/model_inheritance_regress/tests.py b/tests/model_inheritance_regress/tests.py index 8f741bbb7f..98216dbc84 100644 --- a/tests/model_inheritance_regress/tests.py +++ b/tests/model_inheritance_regress/tests.py @@ -8,6 +8,7 @@ from operator import attrgetter from django import forms from django.test import TestCase +from django.utils.unittest import expectedFailure from .models import (Place, Restaurant, ItalianRestaurant, ParkingLot, ParkingLot2, ParkingLot3, Supplier, Wholesaler, Child, SelfRefParent, @@ -422,3 +423,19 @@ class ModelInheritanceTest(TestCase): form = ProfileForm({'username': "user_with_profile", 'extra': "hello"}, instance=p) self.assertTrue(form.is_valid()) + + def test_inheritance_joins(self): + # Test for #17502 - check that filtering through two levels of + # inheritance chain doesn't generate extra joins. + qs = ItalianRestaurant.objects.all() + self.assertEqual(str(qs.query).count('JOIN'), 2) + qs = ItalianRestaurant.objects.filter(name='foo') + self.assertEqual(str(qs.query).count('JOIN'), 2) + + @expectedFailure + def test_inheritance_values_joins(self): + # It would be nice (but not too important) to skip the middle join in + # this case. Skipping is possible as nothing from the middle model is + # used in the qs and top contains direct pointer to the bottom model. + qs = ItalianRestaurant.objects.values_list('serves_gnocchi').filter(name='foo') + self.assertEqual(str(qs.query).count('JOIN'), 1)