1
0
mirror of https://github.com/django/django.git synced 2025-06-05 11:39:13 +00:00

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.
This commit is contained in:
Anssi Kääriäinen 2012-05-09 20:33:31 +03:00
parent 1b05546bd5
commit c2e1ecb4b1
4 changed files with 27 additions and 19 deletions

View File

@ -261,12 +261,12 @@ class SQLCompiler(object):
result = [] result = []
if opts is None: if opts is None:
opts = self.query.model._meta opts = self.query.model._meta
# Skip all proxy to the root proxied model
opts = opts.concrete_model._meta
qn = self.quote_name_unless_alias qn = self.quote_name_unless_alias
qn2 = self.connection.ops.quote_name qn2 = self.connection.ops.quote_name
aliases = set() aliases = set()
only_load = self.deferred_to_columns() only_load = self.deferred_to_columns()
# Skip all proxy to the root proxied model
proxied_model = opts.concrete_model
if start_alias: if start_alias:
seen = {None: start_alias} seen = {None: start_alias}
@ -277,12 +277,9 @@ class SQLCompiler(object):
try: try:
alias = seen[model] alias = seen[model]
except KeyError: except KeyError:
if model is proxied_model: link_field = opts.get_ancestor_link(model)
alias = start_alias alias = self.query.join((start_alias, model._meta.db_table,
else: link_field.column, model._meta.pk.column))
link_field = opts.get_ancestor_link(model)
alias = self.query.join((start_alias, model._meta.db_table,
link_field.column, model._meta.pk.column))
seen[model] = alias seen[model] = alias
else: else:
# If we're starting from the base model of the queryset, the # If we're starting from the base model of the queryset, the

View File

@ -933,21 +933,16 @@ class Query(object):
whereas column determination is a later part, and side-effect, of whereas column determination is a later part, and side-effect, of
as_sql()). as_sql()).
""" """
opts = self.model._meta # Skip all proxy models
opts = self.model._meta.concrete_model._meta
root_alias = self.tables[0] root_alias = self.tables[0]
seen = {None: root_alias} 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(): for field, model in opts.get_fields_with_model():
if model not in seen: if model not in seen:
if model is proxied_model: link_field = opts.get_ancestor_link(model)
seen[model] = root_alias seen[model] = self.join((root_alias, model._meta.db_table,
else: link_field.column, model._meta.pk.column))
link_field = opts.get_ancestor_link(model)
seen[model] = self.join((root_alias, model._meta.db_table,
link_field.column, model._meta.pk.column))
self.included_inherited_models = seen self.included_inherited_models = seen
def remove_inherited_models(self): def remove_inherited_models(self):

View File

@ -10,6 +10,10 @@ from django.db import models
class DumbCategory(models.Model): class DumbCategory(models.Model):
pass pass
class ProxyCategory(DumbCategory):
class Meta:
proxy = True
class NamedCategory(DumbCategory): class NamedCategory(DumbCategory):
name = models.CharField(max_length=10) name = models.CharField(max_length=10)

View File

@ -19,7 +19,7 @@ from .models import (Annotation, Article, Author, Celebrity, Child, Cover,
ManagedModel, Member, NamedCategory, Note, Number, Plaything, PointerA, ManagedModel, Member, NamedCategory, Note, Number, Plaything, PointerA,
Ranking, Related, Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten, Ranking, Related, Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten,
Node, ObjectA, ObjectB, ObjectC, CategoryItem, SimpleCategory, Node, ObjectA, ObjectB, ObjectC, CategoryItem, SimpleCategory,
SpecialCategory, OneToOneCategory, NullableName) SpecialCategory, OneToOneCategory, NullableName, ProxyCategory)
class BaseQuerysetTest(TestCase): class BaseQuerysetTest(TestCase):
@ -1952,3 +1952,15 @@ class EmptyStringsAsNullTest(TestCase):
DumbCategory.objects.exclude(namedcategory__name__in=['nonexisting']), DumbCategory.objects.exclude(namedcategory__name__in=['nonexisting']),
[self.nc.pk], attrgetter('pk') [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)