summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Meyer <carl@oddbird.net>2012-02-22 05:26:50 +0000
committerCarl Meyer <carl@oddbird.net>2012-02-22 05:26:50 +0000
commit354c84d277e3a9516cd2af1567eb02cb4da1a3e4 (patch)
tree5e72bff88028889e482ea5b8b865c8886f17add0
parent3ac0961e1ec3f935474e3c1c3f16bee1cc5ae54a (diff)
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
-rw-r--r--django/contrib/contenttypes/models.py6
-rw-r--r--django/contrib/gis/db/models/sql/compiler.py3
-rw-r--r--django/db/models/base.py9
-rw-r--r--django/db/models/options.py9
-rw-r--r--django/db/models/sql/compiler.py4
-rw-r--r--django/db/models/sql/query.py19
-rw-r--r--tests/modeltests/proxy_models/tests.py6
7 files changed, 27 insertions, 29 deletions
diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py
index 6d919b4237..d588ff4d23 100644
--- a/django/contrib/contenttypes/models.py
+++ b/django/contrib/contenttypes/models.py
@@ -17,11 +17,7 @@ class ContentTypeManager(models.Manager):
return ct
def _get_opts(self, model):
- opts = model._meta
- while opts.proxy:
- model = opts.proxy_for_model
- opts = model._meta
- return opts
+ return model._meta.concrete_model._meta
def _get_from_cache(self, opts):
key = (opts.app_label, opts.object_name.lower())
diff --git a/django/contrib/gis/db/models/sql/compiler.py b/django/contrib/gis/db/models/sql/compiler.py
index a7c3fbc406..07eea32b69 100644
--- a/django/contrib/gis/db/models/sql/compiler.py
+++ b/django/contrib/gis/db/models/sql/compiler.py
@@ -2,7 +2,6 @@ from itertools import izip
from django.db.backends.util import truncate_name, typecast_timestamp
from django.db.models.sql import compiler
from django.db.models.sql.constants import TABLE_NAME, MULTI
-from django.db.models.sql.query import get_proxied_model
SQLCompiler = compiler.SQLCompiler
@@ -116,7 +115,7 @@ class GeoSQLCompiler(compiler.SQLCompiler):
aliases = set()
only_load = self.deferred_to_columns()
# Skip all proxy to the root proxied model
- proxied_model = get_proxied_model(opts)
+ proxied_model = opts.concrete_model
if start_alias:
seen = {None: start_alias}
diff --git a/django/db/models/base.py b/django/db/models/base.py
index ebd67bef05..fc38224345 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -122,9 +122,10 @@ class ModelBase(type):
if (new_class._meta.local_fields or
new_class._meta.local_many_to_many):
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.concrete_model = base._meta.concrete_model
+ else:
+ new_class._meta.concrete_model = new_class
# Do the appropriate setup for any model parents.
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__))
if not base._meta.abstract:
# Concrete classes...
- while base._meta.proxy:
- # Skip over a proxy class to the "real" base it proxies.
- base = base._meta.proxy_for_model
+ base = base._meta.concrete_model
if base in o2o_map:
field = o2o_map[base]
elif not is_proxy:
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 0cd52a3122..f68473de94 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -40,7 +40,16 @@ class Options(object):
self.abstract = False
self.managed = True
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
+ # 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.duplicate_targets = {}
self.auto_created = False
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 72948f9cd7..6c516e2b21 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -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.datastructures import EmptyResultSet
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
@@ -266,7 +266,7 @@ class SQLCompiler(object):
aliases = set()
only_load = self.deferred_to_columns()
# Skip all proxy to the root proxied model
- proxied_model = get_proxied_model(opts)
+ proxied_model = opts.concrete_model
if start_alias:
seen = {None: start_alias}
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index a78df343a7..693dde3b40 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -575,10 +575,7 @@ class Query(object):
return
orig_opts = self.model._meta
seen = {}
- if orig_opts.proxy:
- must_include = {orig_opts.proxy_for_model: set([orig_opts.pk])}
- else:
- must_include = {self.model: set([orig_opts.pk])}
+ must_include = {orig_opts.concrete_model: set([orig_opts.pk])}
for field_name in field_names:
parts = field_name.split(LOOKUP_SEP)
cur_model = self.model
@@ -586,7 +583,7 @@ class Query(object):
for name in parts[:-1]:
old_model = cur_model
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
# Even if we're "just passing through" this model, we must add
# both the current model's pk and the related reference field
@@ -946,7 +943,7 @@ class Query(object):
seen = {None: root_alias}
# 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():
if model not in seen:
@@ -1325,7 +1322,7 @@ class Query(object):
if model:
# The field lives on a base class of the current 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):
if int_model is proxied_model:
@@ -1990,11 +1987,3 @@ def add_to_dict(data, key, value):
data[key].add(value)
else:
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
diff --git a/tests/modeltests/proxy_models/tests.py b/tests/modeltests/proxy_models/tests.py
index 3ec8465689..b3321038db 100644
--- a/tests/modeltests/proxy_models/tests.py
+++ b/tests/modeltests/proxy_models/tests.py
@@ -232,6 +232,12 @@ class ProxyModelTests(TestCase):
resp = [u.name for u in UserProxyProxy.objects.all()]
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):
"""
Proxy objects can be deleted