martync martync - 1 month ago 16
Python Question

Django inheritance and parent object related name

I'm upgrading a project from django 1.8 to 1.10 and it looks like django has improved the check of eventual name collision between foreign keys and model inheritance.
This is obviously a good thing, but the projet I need to upgrade is a big one and it would be a hell to rename a model.

Let me explain the problem : I have a base class called

Parent
and many children which are linked together, like so :

class Parent(models.Model):
title = models.CharField(max_length=10)


class ChildA(Parent):
description = models.TextField()


class ChildB(Parent):
description = models.TextField()
childa = models.ForeignKey(ChildA)


The clash here is that a
childb
object has 2 "childa" attributes :


  • The "childa" ForeignKey

  • The instance inherited by the ChildA model (because childb has also the
    parent
    attributes).



The 2 obvious solutions here are :


  • Rename the ForeignKey
    ChildB.childa
    to
    ChildB.somethingelse

  • Rename the
    ChildA
    model to something else.



Both solutions costs a lot and will probably introduce new bugs.
So I wondered : Is it possible to rename the reverse related name of the inherited object ?

For example :

p = Parent.objects.get(pk=1)
print p.childa_child # Hit the ChildA instance


I have no idea if I'm clear enough but I'll keep this question up to date.

==== EDIT ====

To be more concise, if I have 2 models
class Parent(models.Model)
and
class Child(Parent)
, a dynamic attribute
parent.child
is created.

Is it possible to edit this attribute name without touching the class name ?

Answer

Multi-table inheritance creates an implicit OneToOneField field between the base model and the subclass.

Django allows you to modify this relationship by explicitly setting the one to one field.

class Parent(models.Model):
    title = models.CharField(max_length=10)


class ChildA(Parent):
    parent = models.OneToOneField(to=Parent, parent_link=True)
    description = models.TextField()


class ChildB(Parent):
    parent = models.OneToOneField(to=Parent, parent_link=True)
    description = models.TextField()
    childa = models.ForeignKey(ChildA)

The important bit here is the parent_link=True argument which tells Django to use this field declaration for managing the multi-table inheritance with these two models.

So you can now set related_name='+' to prevent Django from creating a reverse relationship or you can set related_name to a more unique name:

class ChildA(Parent):
    parent = models.OneToOneField(to=Parent, parent_link=True, related_name='child_a_here')
    description = models.TextField()


class ChildB(Parent):
    parent = models.OneToOneField(to=Parent, parent_link=True, related_name='child_b_here')
    description = models.TextField()
    childa = models.ForeignKey(ChildA)
Comments