Search code examples
djangodjango-polymorphic

How to convert between models in django-polymorphic?


I'm using django-polymorphic to model a simple relationship like this:

from django.db import models

from polymorphic.models import PolymorphicModel

class Base(PolymorphicModel):
    attribute_base = models.BooleanField()

class DescendantA(Base):
    attribute_a = models.BooleanField()

class DescendantB(Base):
    attribute_b = models.BooleanField()

I would like to find a way to convert an instance of DescendantA to an instance of DescendantB while keeping the data of its Base part (specifically the primary key). In my scenario, related models exist pointing to the instance. I would like to keep these relationships (and the instance's primary key) intact.

Setting the appropriate __class__ and polymorphic_ctype attributes on the instance (see this answer) works mostly but predictably results in inconsistent data: The database row for DescendantA will not be deleted and continues pointing to the Base row.

Is there a clean(er) way to achieve this conversion? I'm inclined to just create a new instance and copy the original data but I hope there's a better way.


Related questions:


Solution

  • I found a way that works pretty well but uses private parts of Django's API and so might break in the future. I wrote some tests that will alert me if this happens.

    The gist of my solution is

    from django.contrib.contenttypes.models import ContentType
    
    # Setup
    a = DescendantA.objects.create()
    
    # Create new child instance
    b = DescendantB(base_ptr=a)
    b.save_base(raw=True)
    
    # Delete the other child instance
    a.delete(keep_parents=True)
    
    # Change class and point to correct content type
    a.__class__ = DescendantB
    a.polymorphic_ctype = ContentType.objects.get_for_model(DescendantB)
    a.save()
    

    Related answers: