Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fix proxy model Query.remove_inherited_models()

Fixed #18248 -- proxy models were added to included_inherited_models
in sql.query.Query. The variable is meant to be used for multitable
inheritance only. This mistake caused problems in situations where
proxy model's query was reused.
  • Loading branch information...
commit c2e1ecb4b136016ab2996712aa0583fd91657bc9 1 parent 1b05546
Anssi Kääriäinen authored
13  django/db/models/sql/compiler.py
@@ -261,12 +261,12 @@ def get_default_columns(self, with_aliases=False, col_aliases=None,
261 261
         result = []
262 262
         if opts is None:
263 263
             opts = self.query.model._meta
  264
+        # Skip all proxy to the root proxied model
  265
+        opts = opts.concrete_model._meta
264 266
         qn = self.quote_name_unless_alias
265 267
         qn2 = self.connection.ops.quote_name
266 268
         aliases = set()
267 269
         only_load = self.deferred_to_columns()
268  
-        # Skip all proxy to the root proxied model
269  
-        proxied_model = opts.concrete_model
270 270
 
271 271
         if start_alias:
272 272
             seen = {None: start_alias}
@@ -277,12 +277,9 @@ def get_default_columns(self, with_aliases=False, col_aliases=None,
277 277
                 try:
278 278
                     alias = seen[model]
279 279
                 except KeyError:
280  
-                    if model is proxied_model:
281  
-                        alias = start_alias
282  
-                    else:
283  
-                        link_field = opts.get_ancestor_link(model)
284  
-                        alias = self.query.join((start_alias, model._meta.db_table,
285  
-                                link_field.column, model._meta.pk.column))
  280
+                    link_field = opts.get_ancestor_link(model)
  281
+                    alias = self.query.join((start_alias, model._meta.db_table,
  282
+                            link_field.column, model._meta.pk.column))
286 283
                     seen[model] = alias
287 284
             else:
288 285
                 # If we're starting from the base model of the queryset, the
15  django/db/models/sql/query.py
@@ -933,21 +933,16 @@ def setup_inherited_models(self):
933 933
         whereas column determination is a later part, and side-effect, of
934 934
         as_sql()).
935 935
         """
936  
-        opts = self.model._meta
  936
+        # Skip all proxy models
  937
+        opts = self.model._meta.concrete_model._meta
937 938
         root_alias = self.tables[0]
938 939
         seen = {None: root_alias}
939 940
 
940  
-        # Skip all proxy to the root proxied model
941  
-        proxied_model = opts.concrete_model
942  
-
943 941
         for field, model in opts.get_fields_with_model():
944 942
             if model not in seen:
945  
-                if model is proxied_model:
946  
-                    seen[model] = root_alias
947  
-                else:
948  
-                    link_field = opts.get_ancestor_link(model)
949  
-                    seen[model] = self.join((root_alias, model._meta.db_table,
950  
-                            link_field.column, model._meta.pk.column))
  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))
951 946
         self.included_inherited_models = seen
952 947
 
953 948
     def remove_inherited_models(self):
4  tests/regressiontests/queries/models.py
@@ -10,6 +10,10 @@
10 10
 class DumbCategory(models.Model):
11 11
     pass
12 12
 
  13
+class ProxyCategory(DumbCategory):
  14
+    class Meta:
  15
+        proxy = True
  16
+
13 17
 class NamedCategory(DumbCategory):
14 18
     name = models.CharField(max_length=10)
15 19
 
14  tests/regressiontests/queries/tests.py
@@ -19,7 +19,7 @@
19 19
     ManagedModel, Member, NamedCategory, Note, Number, Plaything, PointerA,
20 20
     Ranking, Related, Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten,
21 21
     Node, ObjectA, ObjectB, ObjectC, CategoryItem, SimpleCategory,
22  
-    SpecialCategory, OneToOneCategory, NullableName)
  22
+    SpecialCategory, OneToOneCategory, NullableName, ProxyCategory)
23 23
 
24 24
 
25 25
 class BaseQuerysetTest(TestCase):
@@ -1952,3 +1952,15 @@ def test_joined_exclude(self):
1952 1952
             DumbCategory.objects.exclude(namedcategory__name__in=['nonexisting']),
1953 1953
             [self.nc.pk], attrgetter('pk')
1954 1954
         )
  1955
+
  1956
+class ProxyQueryCleanupTest(TestCase):
  1957
+    def test_evaluated_proxy_count(self):
  1958
+        """
  1959
+        Test that generating the query string doesn't alter the query's state
  1960
+        in irreversible ways. Refs #18248.
  1961
+        """
  1962
+        ProxyCategory.objects.create()
  1963
+        qs = ProxyCategory.objects.all()
  1964
+        self.assertEqual(qs.count(), 1)
  1965
+        str(qs.query)
  1966
+        self.assertEqual(qs.count(), 1)

0 notes on commit c2e1ecb

Please sign in to comment.
Something went wrong with that request. Please try again.