diff options
| author | Anssi Kääriäinen <anssi.kaariainen@thl.fi> | 2015-10-27 13:13:16 +0200 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2015-12-14 10:48:01 -0500 |
| commit | cd0ba8053dfdc81ade9a656dc8049de600638501 (patch) | |
| tree | 1e2a19940c9b82440bae8d7abcc6899f265a7f89 /django | |
| parent | 4cd24bb67c8d82630817e97ab9ba444ca955a94d (diff) | |
Fixed #12885 -- Fixed queries with GenericRelations to multi-table inheritance child models.
Diffstat (limited to 'django')
| -rw-r--r-- | django/contrib/contenttypes/fields.py | 40 |
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 |
