Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert a subclass model instance to another subclass model instance in django?

Tags:

django

model

I have a ModelBase, and ModelA, ModelB.

I want to change ModelA instance to ModelB instance. (I can handle the difference of attributes they have)

I've seen related questions but doesn't quite work for me.

How can I create an inherited django model instance from an existing base model instance?
Change class of child on django models

  • EDIT

When you have Place - Restaurant/Bar relationship,
I think it's quite reasonable to be able to switch a restaurant to a bar.

like image 995
eugene Avatar asked Mar 21 '23 00:03

eugene


2 Answers

I had to deal with the same problem, both yuvi and arctelix answers did not work for me. yuvi solution gives an error and arctelix solution creates new object with new pk.

The goal here is to change the subclass model while keeping the original superclass as it is with the old pk.

First: Delete the old subclass and keep the superclass.Check Django documents.

Second: Add the new subclass with its fields and pass the superclass to it. Check this q

Example: A place could be a restaurant or a caffe, and you want to change a restaurant place to a caffee; as follow:

class Place(models.Model):
            name = models.CharField(max_length=50)
            address = models.CharField(max_length=80)

class Caffe(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

class Restaurant(Place):
    serves_tea = models.BooleanField(default=False)
    serves_coffee = models.BooleanField(default=False)

# get the objecte to be changed
rest = Restaurant.objects.get(pk=1) #arbitrary number
#delete the subclass while keeping the parent
rest.delete(keep_parents=True)

place = Place.objects.get(pk=1) # the primary key must be the same as the deleted restaurant

# Create a caffe and pass the original place
caffee = Caffe(place_ptr_id=place.pk) #this will empty the parent field

#update parent fields
caffee.__dict__.update(place.__dict__)

#add other field
........

#save the caffe
caffee.save()
like image 134
Salman Aljabri Avatar answered Apr 25 '23 06:04

Salman Aljabri


I would create an entirely new instance of the second model with the same values of their shared attributes, then delete the old one. Seems like the cleanest way to me.

If ModelBase is abstract:

instance = ModelA.objects.get(pk=1) #arbitrary        

# find parent class fields:
fields = [f.name for f in ModelBase._meta.fields]

# get the values from the modelA instance
values = dict( [(x, getattr(instance, x)) for x in fields] )

#assign same values to new instance of second model
new_instance = ModelB(**values) 

#add any additional information to new instance here

new_instance.save() #save new one
instance.delete() # remove the old one

If ModelBase is not abstract, however, you'll have to do an extra workaround:

fields = [f.name for f in ModelBase._meta.fields if f.name != 'id']
#... other parts are the same...

new_instance.modelbase_ptr = instance.modelbase_ptr #re-assign related parent
instance.delete() #delete this first!
new_instance.save()
like image 38
yuvi Avatar answered Apr 25 '23 07:04

yuvi