summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2015-10-01 14:57:58 -0400
committerSimon Charette <charette.s@gmail.com>2015-10-12 12:14:26 -0400
commit6c9f37ea9eafb0ca1b02eb71ae8d375672043824 (patch)
treeac1a374541c6a345fe73a0d5a86ff2881b7bc33a /django
parentc8f091f5bcce317629c4af86c88a2864ab7d380b (diff)
Fixed #18012 -- Propagated reverse foreign keys from proxy to concrete models.
Thanks to Anssi for the review.
Diffstat (limited to 'django')
-rw-r--r--django/db/models/fields/related.py2
-rw-r--r--django/db/models/fields/related_descriptors.py2
-rw-r--r--django/db/models/options.py13
3 files changed, 12 insertions, 5 deletions
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index ac6b8221df..24bab96df6 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -684,7 +684,7 @@ class ForeignObject(RelatedField):
# Internal FK's - i.e., those with a related name ending with '+' -
# and swapped models don't get a related descriptor.
if not self.remote_field.is_hidden() and not related.related_model._meta.swapped:
- setattr(cls, related.get_accessor_name(), self.related_accessor_class(related))
+ setattr(cls._meta.concrete_model, related.get_accessor_name(), self.related_accessor_class(related))
# While 'limit_choices_to' might be a callable, simply pass
# it along for later - this is too early because it's still
# model load time.
diff --git a/django/db/models/fields/related_descriptors.py b/django/db/models/fields/related_descriptors.py
index e9caa2680f..486829386f 100644
--- a/django/db/models/fields/related_descriptors.py
+++ b/django/db/models/fields/related_descriptors.py
@@ -198,7 +198,7 @@ class ForwardManyToOneDescriptor(object):
'Cannot assign None: "%s.%s" does not allow null values.' %
(instance._meta.object_name, self.field.name)
)
- elif value is not None and not isinstance(value, self.field.remote_field.model):
+ elif value is not None and not isinstance(value, self.field.remote_field.model._meta.concrete_model):
raise ValueError(
'Cannot assign "%r": "%s.%s" must be a "%s" instance.' % (
value,
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 28c2088630..28773bc0b3 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -552,15 +552,20 @@ class Options(object):
is set as a property on every model.
"""
related_objects_graph = defaultdict(list)
+ # Map of concrete models to all options of models it represents.
+ # Including its options and all its proxy model ones.
+ concrete_model_classes = defaultdict(list)
all_models = self.apps.get_models(include_auto_created=True)
for model in all_models:
+ opts = model._meta
+ concrete_model_classes[opts.concrete_model].append(opts)
# Abstract model's fields are copied to child models, hence we will
# see the fields from the child models.
- if model._meta.abstract:
+ if opts.abstract:
continue
fields_with_relations = (
- f for f in model._meta._get_fields(reverse=False, include_parents=False)
+ f for f in opts._get_fields(reverse=False, include_parents=False)
if f.is_relation and f.related_model is not None
)
for f in fields_with_relations:
@@ -573,7 +578,9 @@ class Options(object):
# __dict__ takes precedence over a data descriptor (such as
# @cached_property). This means that the _meta._relation_tree is
# only called if related_objects is not in __dict__.
- related_objects = related_objects_graph[model._meta]
+ related_objects = list(chain.from_iterable(
+ related_objects_graph[opts] for opts in concrete_model_classes[model]
+ ))
model._meta.__dict__['_relation_tree'] = related_objects
# It seems it is possible that self is not in all_models, so guard
# against that with default for get().