Fixed #17678 -- Corrected setup of _meta.proxy_for_model and added _meta.concrete_model. Thanks Anssi Kääriäinen.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17573 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
3ac0961e1e
commit
354c84d277
|
@ -17,11 +17,7 @@ class ContentTypeManager(models.Manager):
|
||||||
return ct
|
return ct
|
||||||
|
|
||||||
def _get_opts(self, model):
|
def _get_opts(self, model):
|
||||||
opts = model._meta
|
return model._meta.concrete_model._meta
|
||||||
while opts.proxy:
|
|
||||||
model = opts.proxy_for_model
|
|
||||||
opts = model._meta
|
|
||||||
return opts
|
|
||||||
|
|
||||||
def _get_from_cache(self, opts):
|
def _get_from_cache(self, opts):
|
||||||
key = (opts.app_label, opts.object_name.lower())
|
key = (opts.app_label, opts.object_name.lower())
|
||||||
|
|
|
@ -2,7 +2,6 @@ from itertools import izip
|
||||||
from django.db.backends.util import truncate_name, typecast_timestamp
|
from django.db.backends.util import truncate_name, typecast_timestamp
|
||||||
from django.db.models.sql import compiler
|
from django.db.models.sql import compiler
|
||||||
from django.db.models.sql.constants import TABLE_NAME, MULTI
|
from django.db.models.sql.constants import TABLE_NAME, MULTI
|
||||||
from django.db.models.sql.query import get_proxied_model
|
|
||||||
|
|
||||||
SQLCompiler = compiler.SQLCompiler
|
SQLCompiler = compiler.SQLCompiler
|
||||||
|
|
||||||
|
@ -116,7 +115,7 @@ class GeoSQLCompiler(compiler.SQLCompiler):
|
||||||
aliases = set()
|
aliases = set()
|
||||||
only_load = self.deferred_to_columns()
|
only_load = self.deferred_to_columns()
|
||||||
# Skip all proxy to the root proxied model
|
# Skip all proxy to the root proxied model
|
||||||
proxied_model = get_proxied_model(opts)
|
proxied_model = opts.concrete_model
|
||||||
|
|
||||||
if start_alias:
|
if start_alias:
|
||||||
seen = {None: start_alias}
|
seen = {None: start_alias}
|
||||||
|
|
|
@ -122,9 +122,10 @@ class ModelBase(type):
|
||||||
if (new_class._meta.local_fields or
|
if (new_class._meta.local_fields or
|
||||||
new_class._meta.local_many_to_many):
|
new_class._meta.local_many_to_many):
|
||||||
raise FieldError("Proxy model '%s' contains model fields." % name)
|
raise FieldError("Proxy model '%s' contains model fields." % name)
|
||||||
while base._meta.proxy:
|
|
||||||
base = base._meta.proxy_for_model
|
|
||||||
new_class._meta.setup_proxy(base)
|
new_class._meta.setup_proxy(base)
|
||||||
|
new_class._meta.concrete_model = base._meta.concrete_model
|
||||||
|
else:
|
||||||
|
new_class._meta.concrete_model = new_class
|
||||||
|
|
||||||
# Do the appropriate setup for any model parents.
|
# Do the appropriate setup for any model parents.
|
||||||
o2o_map = dict([(f.rel.to, f) for f in new_class._meta.local_fields
|
o2o_map = dict([(f.rel.to, f) for f in new_class._meta.local_fields
|
||||||
|
@ -149,9 +150,7 @@ class ModelBase(type):
|
||||||
(field.name, name, base.__name__))
|
(field.name, name, base.__name__))
|
||||||
if not base._meta.abstract:
|
if not base._meta.abstract:
|
||||||
# Concrete classes...
|
# Concrete classes...
|
||||||
while base._meta.proxy:
|
base = base._meta.concrete_model
|
||||||
# Skip over a proxy class to the "real" base it proxies.
|
|
||||||
base = base._meta.proxy_for_model
|
|
||||||
if base in o2o_map:
|
if base in o2o_map:
|
||||||
field = o2o_map[base]
|
field = o2o_map[base]
|
||||||
elif not is_proxy:
|
elif not is_proxy:
|
||||||
|
|
|
@ -40,7 +40,16 @@ class Options(object):
|
||||||
self.abstract = False
|
self.abstract = False
|
||||||
self.managed = True
|
self.managed = True
|
||||||
self.proxy = False
|
self.proxy = False
|
||||||
|
# For any class which is a proxy (including automatically created
|
||||||
|
# classes for deferred object loading) the proxy_for_model tells
|
||||||
|
# which class this model is proxying. Note that proxy_for_model
|
||||||
|
# can create a chain of proxy models. For non-proxy models the
|
||||||
|
# variable is always None.
|
||||||
self.proxy_for_model = None
|
self.proxy_for_model = None
|
||||||
|
# For any non-abstract class the concrete class is the model
|
||||||
|
# in the end of the proxy_for_model chain. In particular, for
|
||||||
|
# concrete models the concrete_model is always the class itself.
|
||||||
|
self.concrete_model = None
|
||||||
self.parents = SortedDict()
|
self.parents = SortedDict()
|
||||||
self.duplicate_targets = {}
|
self.duplicate_targets = {}
|
||||||
self.auto_created = False
|
self.auto_created = False
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.db.models.query_utils import select_related_descend
|
||||||
from django.db.models.sql.constants import *
|
from django.db.models.sql.constants import *
|
||||||
from django.db.models.sql.datastructures import EmptyResultSet
|
from django.db.models.sql.datastructures import EmptyResultSet
|
||||||
from django.db.models.sql.expressions import SQLEvaluator
|
from django.db.models.sql.expressions import SQLEvaluator
|
||||||
from django.db.models.sql.query import get_proxied_model, get_order_dir, Query
|
from django.db.models.sql.query import get_order_dir, Query
|
||||||
from django.db.utils import DatabaseError
|
from django.db.utils import DatabaseError
|
||||||
|
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ class SQLCompiler(object):
|
||||||
aliases = set()
|
aliases = set()
|
||||||
only_load = self.deferred_to_columns()
|
only_load = self.deferred_to_columns()
|
||||||
# Skip all proxy to the root proxied model
|
# Skip all proxy to the root proxied model
|
||||||
proxied_model = get_proxied_model(opts)
|
proxied_model = opts.concrete_model
|
||||||
|
|
||||||
if start_alias:
|
if start_alias:
|
||||||
seen = {None: start_alias}
|
seen = {None: start_alias}
|
||||||
|
|
|
@ -575,10 +575,7 @@ class Query(object):
|
||||||
return
|
return
|
||||||
orig_opts = self.model._meta
|
orig_opts = self.model._meta
|
||||||
seen = {}
|
seen = {}
|
||||||
if orig_opts.proxy:
|
must_include = {orig_opts.concrete_model: set([orig_opts.pk])}
|
||||||
must_include = {orig_opts.proxy_for_model: set([orig_opts.pk])}
|
|
||||||
else:
|
|
||||||
must_include = {self.model: set([orig_opts.pk])}
|
|
||||||
for field_name in field_names:
|
for field_name in field_names:
|
||||||
parts = field_name.split(LOOKUP_SEP)
|
parts = field_name.split(LOOKUP_SEP)
|
||||||
cur_model = self.model
|
cur_model = self.model
|
||||||
|
@ -586,7 +583,7 @@ class Query(object):
|
||||||
for name in parts[:-1]:
|
for name in parts[:-1]:
|
||||||
old_model = cur_model
|
old_model = cur_model
|
||||||
source = opts.get_field_by_name(name)[0]
|
source = opts.get_field_by_name(name)[0]
|
||||||
cur_model = opts.get_field_by_name(name)[0].rel.to
|
cur_model = source.rel.to
|
||||||
opts = cur_model._meta
|
opts = cur_model._meta
|
||||||
# Even if we're "just passing through" this model, we must add
|
# Even if we're "just passing through" this model, we must add
|
||||||
# both the current model's pk and the related reference field
|
# both the current model's pk and the related reference field
|
||||||
|
@ -946,7 +943,7 @@ class Query(object):
|
||||||
seen = {None: root_alias}
|
seen = {None: root_alias}
|
||||||
|
|
||||||
# Skip all proxy to the root proxied model
|
# Skip all proxy to the root proxied model
|
||||||
proxied_model = get_proxied_model(opts)
|
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:
|
||||||
|
@ -1325,7 +1322,7 @@ class Query(object):
|
||||||
if model:
|
if model:
|
||||||
# The field lives on a base class of the current model.
|
# The field lives on a base class of the current model.
|
||||||
# Skip the chain of proxy to the concrete proxied model
|
# Skip the chain of proxy to the concrete proxied model
|
||||||
proxied_model = get_proxied_model(opts)
|
proxied_model = opts.concrete_model
|
||||||
|
|
||||||
for int_model in opts.get_base_chain(model):
|
for int_model in opts.get_base_chain(model):
|
||||||
if int_model is proxied_model:
|
if int_model is proxied_model:
|
||||||
|
@ -1990,11 +1987,3 @@ def add_to_dict(data, key, value):
|
||||||
data[key].add(value)
|
data[key].add(value)
|
||||||
else:
|
else:
|
||||||
data[key] = set([value])
|
data[key] = set([value])
|
||||||
|
|
||||||
def get_proxied_model(opts):
|
|
||||||
int_opts = opts
|
|
||||||
proxied_model = None
|
|
||||||
while int_opts.proxy:
|
|
||||||
proxied_model = int_opts.proxy_for_model
|
|
||||||
int_opts = proxied_model._meta
|
|
||||||
return proxied_model
|
|
||||||
|
|
|
@ -232,6 +232,12 @@ class ProxyModelTests(TestCase):
|
||||||
resp = [u.name for u in UserProxyProxy.objects.all()]
|
resp = [u.name for u in UserProxyProxy.objects.all()]
|
||||||
self.assertEqual(resp, ['Bruce'])
|
self.assertEqual(resp, ['Bruce'])
|
||||||
|
|
||||||
|
def test_proxy_for_model(self):
|
||||||
|
self.assertEqual(UserProxy, UserProxyProxy._meta.proxy_for_model)
|
||||||
|
|
||||||
|
def test_concrete_model(self):
|
||||||
|
self.assertEqual(User, UserProxyProxy._meta.concrete_model)
|
||||||
|
|
||||||
def test_proxy_delete(self):
|
def test_proxy_delete(self):
|
||||||
"""
|
"""
|
||||||
Proxy objects can be deleted
|
Proxy objects can be deleted
|
||||||
|
|
Loading…
Reference in New Issue