Commit c2e1ecb4 authored by Anssi Kääriäinen's avatar Anssi Kääriäinen
Browse files

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.
parent 1b05546b
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
@@ -261,12 +261,12 @@ class SQLCompiler(object):
        result = []
        if opts is None:
            opts = self.query.model._meta
        # Skip all proxy to the root proxied model
        opts = opts.concrete_model._meta
        qn = self.quote_name_unless_alias
        qn2 = self.connection.ops.quote_name
        aliases = set()
        only_load = self.deferred_to_columns()
        # Skip all proxy to the root proxied model
        proxied_model = opts.concrete_model

        if start_alias:
            seen = {None: start_alias}
@@ -277,9 +277,6 @@ class SQLCompiler(object):
                try:
                    alias = seen[model]
                except KeyError:
                    if model is proxied_model:
                        alias = start_alias
                    else:
                    link_field = opts.get_ancestor_link(model)
                    alias = self.query.join((start_alias, model._meta.db_table,
                            link_field.column, model._meta.pk.column))
+5 −10
Original line number Diff line number Diff line
@@ -933,18 +933,13 @@ class Query(object):
        whereas column determination is a later part, and side-effect, of
        as_sql()).
        """
        opts = self.model._meta
        # Skip all proxy models
        opts = self.model._meta.concrete_model._meta
        root_alias = self.tables[0]
        seen = {None: root_alias}

        # Skip all proxy to the root proxied model
        proxied_model = opts.concrete_model

        for field, model in opts.get_fields_with_model():
            if model not in seen:
                if model is proxied_model:
                    seen[model] = root_alias
                else:
                link_field = opts.get_ancestor_link(model)
                seen[model] = self.join((root_alias, model._meta.db_table,
                        link_field.column, model._meta.pk.column))
+4 −0
Original line number Diff line number Diff line
@@ -10,6 +10,10 @@ from django.db import models
class DumbCategory(models.Model):
    pass

class ProxyCategory(DumbCategory):
    class Meta:
        proxy = True

class NamedCategory(DumbCategory):
    name = models.CharField(max_length=10)

+13 −1
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ from .models import (Annotation, Article, Author, Celebrity, Child, Cover,
    ManagedModel, Member, NamedCategory, Note, Number, Plaything, PointerA,
    Ranking, Related, Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten,
    Node, ObjectA, ObjectB, ObjectC, CategoryItem, SimpleCategory,
    SpecialCategory, OneToOneCategory, NullableName)
    SpecialCategory, OneToOneCategory, NullableName, ProxyCategory)


class BaseQuerysetTest(TestCase):
@@ -1952,3 +1952,15 @@ class EmptyStringsAsNullTest(TestCase):
            DumbCategory.objects.exclude(namedcategory__name__in=['nonexisting']),
            [self.nc.pk], attrgetter('pk')
        )

class ProxyQueryCleanupTest(TestCase):
    def test_evaluated_proxy_count(self):
        """
        Test that generating the query string doesn't alter the query's state
        in irreversible ways. Refs #18248.
        """
        ProxyCategory.objects.create()
        qs = ProxyCategory.objects.all()
        self.assertEqual(qs.count(), 1)
        str(qs.query)
        self.assertEqual(qs.count(), 1)