Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 744
martync Avatar asked Oct 31 '16 13:10

martync


Video Answer


1 Answers

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)
like image 73
Paulo Avatar answered Sep 23 '22 20:09

Paulo