28

I'm using multi-table-inheritance, and want to know how to create an inherited type from an instance of the superclass.

Using the example given in the documentation:

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

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField()
    serves_pizza = models.BooleanField()

Now when you create a Restaurant, you automatically make a Place, which is fine, and the behaviour I expect and want.

But what if I make a Place, and later decide I want to convert to a specific type (like Restaurant). How do you create a Restaurant, using an existing Place?

Selcuk
  • 57,004
  • 12
  • 102
  • 110
michel.iamit
  • 5,788
  • 9
  • 55
  • 74
  • 3
    possible duplicate of [Django model inheritance: create sub-instance of existing instance (downcast)?](http://stackoverflow.com/questions/4064808/django-model-inheritance-create-sub-instance-of-existing-instance-downcast) – jpic Mar 22 '12 at 12:47
  • I saw that question and answer, but was confused because I thought that problem had to do with authentication (because a User is a bit more of a special object) – michel.iamit Mar 22 '12 at 13:00
  • I upvoted your duplication remark, however... the question refering to is about Users, which might be confusing. I find this example of places and restaurants (stolen from the Django Documentations) more clear and resembling my problem.... but in base it's the same problem. – michel.iamit Mar 22 '12 at 13:08
  • See https://code.djangoproject.com/ticket/7623 – Sergey Orshanskiy Jun 19 '14 at 16:22

3 Answers3

32

Multi-table inheritance is just OneToOneField relation between Place and Restaurant.

place = Place.objects.get(id=1)               

# Create a restaurant using existing Place   
restaurant = Resturant(place_ptr=place)
restaurant.save()
Mariusz Jamro
  • 30,615
  • 24
  • 120
  • 162
  • 9
    This will cause restaurant.name to be emptied. Because restaurant.save() will update the Place with name='' (because restaurant = Resturant())... there must be a better way... – jpic Mar 22 '12 at 12:36
  • 10
    You should add restaurant.__dict__.update(place.__dict__), see [Daniel Roseman's answer](http://stackoverflow.com/questions/4064808/django-model-inheritance-create-sub-instance-of-existing-instance-downcast). – jpic Mar 22 '12 at 12:47
  • 1
    Ok, the combination of the answer of secator and addition of jpic does the trick... – michel.iamit Mar 22 '12 at 13:03
13
place = Place.objects.get(id=1)

# Create a restaurant using existing Place
place.__class__ = Restaurant
place.save()
restaurant = place
7

While undocumented, this seems to do the trick:

restaurant(place_ptr=place).save_base(raw=True)

This solves the problem without using any hacks and is the shortest solution, also in terms of processing, using Django APIs.

While searching for this solution, I also found a slightly longer one, but using documented APIs. It is basically the same as Mariusz answer, also see this answer for more details:

from django.forms.models import model_to_dict

restaurant(place_ptr=place, **model_to_dict(place)).save()

However, this second one is more risky due to limited field set returned by the model_to_dict (see again the answer explaining the differences among various methods presented). Naturally, it also generates more DB calls because it writes to both tables.

velis
  • 8,747
  • 4
  • 44
  • 64