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 = []
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,12 +277,9 @@ 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))
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
else:
# 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
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))
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
def remove_inherited_models(self):

View File

@ -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)

View File

@ -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)