summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorAnssi Kääriäinen <anssi.kaariainen@thl.fi>2015-10-27 13:13:16 +0200
committerTim Graham <timograham@gmail.com>2015-12-14 10:48:01 -0500
commitcd0ba8053dfdc81ade9a656dc8049de600638501 (patch)
tree1e2a19940c9b82440bae8d7abcc6899f265a7f89 /django
parent4cd24bb67c8d82630817e97ab9ba444ca955a94d (diff)
Fixed #12885 -- Fixed queries with GenericRelations to multi-table inheritance child models.
Diffstat (limited to 'django')
-rw-r--r--django/contrib/contenttypes/fields.py40
1 files changed, 38 insertions, 2 deletions
diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py
index f40c2c713c..0242c148fa 100644
--- a/django/contrib/contenttypes/fields.py
+++ b/django/contrib/contenttypes/fields.py
@@ -359,10 +359,46 @@ class GenericRelation(ForeignObject):
self.to_fields = [self.model._meta.pk.name]
return [(self.remote_field.model._meta.get_field(self.object_id_field_name), self.model._meta.pk)]
+ def _get_path_info_with_parent(self):
+ """
+ Return the path that joins the current model through any parent models.
+ The idea is that if you have a GFK defined on a parent model then we
+ need to join the parent model first, then the child model.
+ """
+ # With an inheritance chain ChildTag -> Tag and Tag defines the
+ # GenericForeignKey, and a TaggedItem model has a GenericRelation to
+ # ChildTag, then we need to generate a join from TaggedItem to Tag
+ # (as Tag.object_id == TaggedItem.pk), and another join from Tag to
+ # ChildTag (as that is where the relation is to). Do this by first
+ # generating a join to the parent model, then generating joins to the
+ # child models.
+ path = []
+ opts = self.remote_field.model._meta
+ parent_opts = opts.get_field(self.object_id_field_name).model._meta
+ target = parent_opts.pk
+ path.append(PathInfo(self.model._meta, parent_opts, (target,), self.remote_field, True, False))
+ # Collect joins needed for the parent -> child chain. This is easiest
+ # to do if we collect joins for the child -> parent chain and then
+ # reverse the direction (call to reverse() and use of
+ # field.remote_field.get_path_info()).
+ parent_field_chain = []
+ while parent_opts != opts:
+ field = opts.get_ancestor_link(parent_opts.model)
+ parent_field_chain.append(field)
+ opts = field.remote_field.model._meta
+ parent_field_chain.reverse()
+ for field in parent_field_chain:
+ path.extend(field.remote_field.get_path_info())
+ return path
+
def get_path_info(self):
opts = self.remote_field.model._meta
- target = opts.pk
- return [PathInfo(self.model._meta, opts, (target,), self.remote_field, True, False)]
+ object_id_field = opts.get_field(self.object_id_field_name)
+ if object_id_field.model != opts.model:
+ return self._get_path_info_with_parent()
+ else:
+ target = opts.pk
+ return [PathInfo(self.model._meta, opts, (target,), self.remote_field, True, False)]
def get_reverse_path_info(self):
opts = self.model._meta