diff options
| author | Simon Charette <charette.s@gmail.com> | 2015-10-01 14:57:58 -0400 |
|---|---|---|
| committer | Simon Charette <charette.s@gmail.com> | 2015-10-12 12:14:26 -0400 |
| commit | 6c9f37ea9eafb0ca1b02eb71ae8d375672043824 (patch) | |
| tree | ac1a374541c6a345fe73a0d5a86ff2881b7bc33a /django | |
| parent | c8f091f5bcce317629c4af86c88a2864ab7d380b (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.py | 2 | ||||
| -rw-r--r-- | django/db/models/fields/related_descriptors.py | 2 | ||||
| -rw-r--r-- | django/db/models/options.py | 13 |
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(). |
