Code

Ticket #17678: 17678.diff

File 17678.diff, 5.1 KB (added by akaariai, 2 years ago)
Line 
1diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py
2index 6d919b4..6e99632 100644
3--- a/django/contrib/contenttypes/models.py
4+++ b/django/contrib/contenttypes/models.py
5@@ -17,11 +17,7 @@ class ContentTypeManager(models.Manager):
6         return ct
7 
8     def _get_opts(self, model):
9-        opts = model._meta
10-        while opts.proxy:
11-            model = opts.proxy_for_model
12-            opts = model._meta
13-        return opts
14+        return model._meta.concrete_class._meta
15 
16     def _get_from_cache(self, opts):
17         key = (opts.app_label, opts.object_name.lower())
18diff --git a/django/db/models/base.py b/django/db/models/base.py
19index ebd67be..1810abd 100644
20--- a/django/db/models/base.py
21+++ b/django/db/models/base.py
22@@ -122,9 +122,10 @@ class ModelBase(type):
23             if (new_class._meta.local_fields or
24                     new_class._meta.local_many_to_many):
25                 raise FieldError("Proxy model '%s' contains model fields." % name)
26-            while base._meta.proxy:
27-                base = base._meta.proxy_for_model
28             new_class._meta.setup_proxy(base)
29+            new_class._meta.concrete_class = base._meta.concrete_class
30+        else:
31+            new_class._meta.concrete_class = new_class
32 
33         # Do the appropriate setup for any model parents.
34         o2o_map = dict([(f.rel.to, f) for f in new_class._meta.local_fields
35@@ -149,9 +150,7 @@ class ModelBase(type):
36                                         (field.name, name, base.__name__))
37             if not base._meta.abstract:
38                 # Concrete classes...
39-                while base._meta.proxy:
40-                    # Skip over a proxy class to the "real" base it proxies.
41-                    base = base._meta.proxy_for_model
42+                base = base._meta.concrete_class
43                 if base in o2o_map:
44                     field = o2o_map[base]
45                 elif not is_proxy:
46diff --git a/django/db/models/options.py b/django/db/models/options.py
47index 0cd52a3..ba3685a 100644
48--- a/django/db/models/options.py
49+++ b/django/db/models/options.py
50@@ -40,7 +40,16 @@ class Options(object):
51         self.abstract = False
52         self.managed = True
53         self.proxy = False
54+        # For any class which is a proxy (including automatically created
55+        # classes for deferred object loading) the proxy_for_model tells
56+        # which class this model is proxying. Note that proxy_for_model
57+        # can create a chain of proxy models. For non-proxy models the
58+        # variable is always None.
59         self.proxy_for_model = None
60+        # For any non-abstract class the concrete class is the model
61+        # in the end of the proxy_for_model chain. In particular, for
62+        # concrete models the concrete_model is always the class itself.
63+        self.concrete_class = None
64         self.parents = SortedDict()
65         self.duplicate_targets = {}
66         self.auto_created = False
67diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
68index a78df34..f7cac1e 100644
69--- a/django/db/models/sql/query.py
70+++ b/django/db/models/sql/query.py
71@@ -575,10 +575,9 @@ class Query(object):
72             return
73         orig_opts = self.model._meta
74         seen = {}
75-        if orig_opts.proxy:
76-            must_include = {orig_opts.proxy_for_model: set([orig_opts.pk])}
77-        else:
78-            must_include = {self.model: set([orig_opts.pk])}
79+        # NOTE: This does what the previous code did. Is this correct? The
80+        # code below is a bit hard to grasp...
81+        must_include = {orig_opts.concrete_class: set([orig_opts.pk])}
82         for field_name in field_names:
83             parts = field_name.split(LOOKUP_SEP)
84             cur_model = self.model
85@@ -586,7 +585,7 @@ class Query(object):
86             for name in parts[:-1]:
87                 old_model = cur_model
88                 source = opts.get_field_by_name(name)[0]
89-                cur_model = opts.get_field_by_name(name)[0].rel.to
90+                cur_model = source.rel.to
91                 opts = cur_model._meta
92                 # Even if we're "just passing through" this model, we must add
93                 # both the current model's pk and the related reference field
94@@ -1992,9 +1991,4 @@ def add_to_dict(data, key, value):
95         data[key] = set([value])
96 
97 def get_proxied_model(opts):
98-    int_opts = opts
99-    proxied_model = None
100-    while int_opts.proxy:
101-        proxied_model = int_opts.proxy_for_model
102-        int_opts = proxied_model._meta
103-    return proxied_model
104+    return opts.concrete_class
105diff --git a/tests/modeltests/proxy_models/tests.py b/tests/modeltests/proxy_models/tests.py
106index 3ec8465..15c5dc0 100644
107--- a/tests/modeltests/proxy_models/tests.py
108+++ b/tests/modeltests/proxy_models/tests.py
109@@ -232,6 +232,9 @@ class ProxyModelTests(TestCase):
110         resp = [u.name for u in UserProxyProxy.objects.all()]
111         self.assertEqual(resp, ['Bruce'])
112 
113+    def test_proxy_for_model(self):
114+        self.assertEquals(UserProxy, UserProxyProxy._meta.proxy_for_model)
115+
116     def test_proxy_delete(self):
117         """
118         Proxy objects can be deleted