Change the polymorphic content type of a django model instance
Asked Answered
V

4

3

If I have a polymorphic model:

class Father(polymorphic.model.PolymorphicModel)

and an inheritor class with no extra fields:

class Child(Father)

When I have an instance of Father, how can I convert it to a Child instance?

What I have tried is:

foo = Father.objects.get(pk=1)
# foo is just a Father, no record in Child database table.
foo.polymorphic_ctype = ContentType.objects.get(app_label='myapp', model='child')
foo.save()

But nothing changes. I want foo to be a Child object and need to have this into the child database table.

Variometer answered 13/9, 2016 at 14:47 Comment(0)
V
4

Ok.

After reading this similar post I realized that I needed another assignment:

foo.__class__ = Child

This makes an insertion in the child table otherwise never happened.

Variometer answered 14/9, 2016 at 15:29 Comment(0)
A
0

Django will automatically duplicate entries into the Child in the Father. For the migration that creates the Child class you can simply save new Child objects in the DB (populating them using the __dict__ property of the python object):

Define this function in your migrate file:

def duplicate_Children(apps,schema_editor):
    Father = apps.get_model('myapp','Father')
    Child= apps.get_model('myapp','Child')
    for dad in Father.objects.all():
         son = Child()

         for attr in dad.__dict__:
             son.__dict__[attr]=dad.__dict__[attr]

         son.save()

...

Add this to the operations list in the Migrate class:

operations = [migrations.RunPython(duplicate_Children)]
Absolutely answered 8/8, 2019 at 7:4 Comment(0)
F
0

For polymorphic model yo can do this:

kclass = YourChangeClass
# With that do the correct insert
self.__class__ = kclass
# With that change the correct model
ct = ContentType.objects.get_for_model(kclass)
self.polymorphic_ctype = ct
# With that set the default values
self.set_default_values()
self.save()

Yo need to define for every class a method that define default values because in other way when do save() fail!.

For example, if you need to pass from A to B:

class A():
    attr_1
class B()
    attr_2
    def set_default_values(self):
        self.attr_2 = None
Formulate answered 8/11, 2019 at 17:55 Comment(0)
H
0

I had trouble with the previous solutions, but based on this post, here's a solution that works with Django-Polymorphic:

ContentType = apps.get_model("contenttypes.ContentType")

queryset = Parent.objects.filter(...)

for parent in queryset:
    # parent_ptr is generated from the name of the parent model
    child = Child(parent_ptr=parent, ...)
    child.save_base(raw=True)

# Change the type in Django-Polymorphic
content_type = ContentType.objects.get_for_model(Child)
queryset.update(polymorphic_ctype=content_type)
Highpressure answered 16/12, 2022 at 4:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.