Multiple inheritance of models in Django

Multiple inheritance of models in Django

Another problem I encountered and decided to share the solution with the rest of the world. The solution was difficult despite digging through stackoverflow and documentation. This solution is obviously based on the knowledge from documentation and stackoverflow. Still, it had to be adapted to my specific problem, namely copying the model object in Django in case of multiple inheritances.

As the django documentation says (doc) just set pk to None:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

And a more comprehensive example for inheritance:

class ThemeBlog(Blog):
    theme = models.CharField(max_length=200)

django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
django_blog.save() # django_blog.pk == 3

 

First of all, in the above example, the documentation lacks "what's next" if we have another inheritance.

In my case, unfortunately, the solution did not work out because I inherit several times. Here's an example that allowed me to understand "how it works" from stackoverflow.

class ModelA(models.Model):
    info1 = models.CharField(max_length=64)

class ModelB(ModelA):
    info2 = models.CharField(max_length=64)

class ModelC(ModelB):
    info3 = models.CharField(max_length=64)

 

And then what?

It turns out that all indirect id's should also be set to None. All fields that have _ptr

c.pk =None
c.id=None
c.modela_ptr_id=None
c.modelb_ptr_id=None
c.save()

Only this approach to the problem results in creating a new id for the copied model. Okay, we have way, but what if the multiple inheritance models are 18, as in my case, and each of the resulting models has a different number of parents? This is how a simple function was created that sets everything "on the way" to the target model to None:

def clear_pks(new_model):
    new_model.pk = None
    new_model.id = None
    for field in new_model._meta.fields:
        if field.name.endswith('_ptr'):
            setattr(mynew, field.name, None)
    return mynew

Hopefully, someone will find my solution and benefit from saving a lot of time.