Ticket #18174: ticket_18174_patch.diff
File ticket_18174_patch.diff, 7.1 KB (added by , 12 years ago) |
---|
-
django/db/models/options.py
diff --git a/django/db/models/options.py b/django/db/models/options.py index 7308a15..540b505 100644
a b class Options(object): 478 478 result.update(parent._meta.get_parent_list()) 479 479 return result 480 480 481 def get_ancestor_link(self, ancestor ):481 def get_ancestor_link(self, ancestor,grandparents=True): 482 482 """ 483 483 Returns the field on the current model which points to the given 484 484 "ancestor". This is possible an indirect link (a pointer to a parent … … class Options(object): 489 489 """ 490 490 if ancestor in self.parents: 491 491 return self.parents[ancestor] 492 if not grandparents: 493 return None 492 494 for parent in self.parents: 493 495 # Tries to get a link field from the immediate parent 494 496 parent_link = parent._meta.get_ancestor_link(ancestor) -
django/db/models/sql/query.py
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 53dad60..df9da5c 100644
a b class Query(object): 877 877 If 'nullable' is True, the join can potentially involve NULL values and 878 878 is a candidate for promotion (to "left outer") when combining querysets. 879 879 """ 880 880 881 lhs, table, lhs_col, col = connection 881 882 if lhs in self.alias_map: 882 883 lhs_table = self.alias_map[lhs].table_name … … class Query(object): 938 939 root_alias = self.tables[0] 939 940 seen = {None: root_alias} 940 941 941 for field, model in opts.get_fields_with_model(): 942 if model not in seen: 943 link_field = opts.get_ancestor_link(model) 944 seen[model] = self.join((root_alias, model._meta.db_table, 945 link_field.column, model._meta.pk.column)) 942 ancestors = [] 943 redo = [i for i in opts.get_fields_with_model()] 944 todo = [] 945 946 # We maintain a list of things todo, and models we coudn't link from this 947 # for the next. If the lenth of both is the same, the previous loop didn't 948 # Link anything, let's bail so we don't end up with an endless loop 949 while len(redo) > 0 and len(redo) != len(todo): 950 todo = redo 951 redo = [] 952 for field, model in todo: 953 link_field = None 954 if model not in seen: 955 link_field = opts.get_ancestor_link(model,grandparents=False) 956 if link_field: 957 seen[model] = self.join((root_alias, model._meta.db_table,link_field.column, model._meta.pk.column)) 958 ancestors.append(model) 959 else: # we didn't link this model to our curent model, try the ancestors 960 for ancestor in ancestors: 961 link_field = ancestor._meta.concrete_model._meta.get_ancestor_link(model,grandparents=False) 962 if link_field: 963 seen[model] = self.join((ancestor._meta.db_table, model._meta.db_table,link_field.column, model._meta.pk.column)) 964 ancestors.append(model) 965 break 966 if not link_field: # didn't find it in the ancestors either, add it to the list to try next time 967 redo.append((field, model)) 968 946 969 self.included_inherited_models = seen 947 970 948 971 def remove_inherited_models(self): -
tests/regressiontests/queries/models.py
diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index 6328776..2eda9e5 100644
a b class NullableName(models.Model): 358 358 359 359 class Meta: 360 360 ordering = ['id'] 361 362 363 class Base1(models.Model): 364 b1_id = models.AutoField(primary_key=True) 365 b1_desc = models.CharField(max_length=100, null=True) 366 367 class Base2(models.Model): 368 b2_id = models.AutoField(primary_key=True) 369 b2_desc = models.CharField(max_length=100, null=True) 370 371 class Base3(models.Model): 372 b3_id = models.AutoField(primary_key=True) 373 b3_desc = models.CharField(max_length=100, null=True) 374 375 class Middle(Base1, Base2, Base3): 376 m_desc = models.CharField(max_length=100, null=True) 377 378 class Top(Middle): 379 t_desc = models.CharField(max_length=100) 380 381 class Top2(Top): 382 t2_desc = models.CharField(max_length=100) 383 384 class Top3(Top2): 385 t3_desc = models.CharField(max_length=100) 386 387 class Top4(Top3): 388 t4_desc = models.CharField(max_length=100) 389 -
tests/regressiontests/queries/tests.py
diff --git a/tests/regressiontests/queries/tests.py b/tests/regressiontests/queries/tests.py index 1582993..03fe879 100644
a b from .models import (Annotation, Article, Author, Celebrity, Child, Cover, 23 23 Ranking, Related, Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten, 24 24 Node, ObjectA, ObjectB, ObjectC, CategoryItem, SimpleCategory, 25 25 SpecialCategory, OneToOneCategory, NullableName, ProxyCategory, 26 SingleObject, RelatedObject )26 SingleObject, RelatedObject, Top, Base1, Top2, Top3, Top4) 27 27 28 28 29 29 class BaseQuerysetTest(TestCase): … … class Queries1Tests(BaseQuerysetTest): 847 847 ) 848 848 Tag._meta.ordering = original_ordering 849 849 850 def test_ticket18174(self): 851 """ 852 Check that in a model like this: 853 / Base1 854 Top -> Middle- Base2 855 \ Base3 856 857 The joins are done from Top to Middle to Base1, Base2, Base3. 858 """ 859 Base1.objects.create() 860 Top.objects.create(t_desc='foo') 861 qs = Top.objects.all() 862 # The joins to the parent tables are added only when the query is 863 # evaluated. 864 results = list(qs) 865 self.assertEqual(qs.query.alias_map['queries_base1'].lhs_alias, 'queries_middle') 866 self.assertEqual(len(results), 1) 867 850 868 def test_exclude(self): 851 869 self.assertQuerysetEqual( 852 870 Item.objects.exclude(tags__name='t4'), … … class Queries1Tests(BaseQuerysetTest): 880 898 Item.objects.filter(Q(tags__name__in=['t4', 't3'])), 881 899 [repr(i) for i in Item.objects.filter(~~Q(tags__name__in=['t4', 't3']))]) 882 900 901 902 def test_ticket18174_2(self): 903 """ 904 Check that in a model like this: 905 / Base1 906 Top4 -> Top3 -> Top2 -> Top -> Middle- Base2 907 \ Base3 908 909 The joins are done from Top to Middle to Base1, Base2, Base3. 910 Adding Extra Grandparents to make sure the chaning to the top works. 911 """ 912 Base1.objects.create() 913 Top4.objects.create(t_desc='foo') 914 qs = Top4.objects.all() 915 # The joins to the parent tables are added only when the query is 916 # evaluated. 917 results = list(qs) 918 self.assertEqual(qs.query.alias_map['queries_base1'].lhs_alias, 'queries_middle') 919 self.assertEqual(len(results), 1) 920 921 883 922 class Queries2Tests(TestCase): 884 923 def setUp(self): 885 924 Number.objects.create(num=4)