Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between a Multi-table inherited model and a simple One-to-one relationship between the same two models?

What is the differences between these implementations? What does Django differently (beside inheriting Meta ordering and get_latest_by attribute) ?

1.

# models.py
from django.db import models

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

class Restaurant(models.Model):
    place = models.OneToOneField(Place)
    serves_pizza = models.BooleanField()

2.

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

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

3.

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

class Restaurant(Place):
    place = models.OneToOneField(Place, parent_link=True)
    serves_pizza = models.BooleanField()
like image 528
kissgyorgy Avatar asked Aug 17 '13 19:08

kissgyorgy


2 Answers

1. You don't really get any python inheritance, that is you can't inherit/override methods or attributes from the model class Place in your class Restaurant:

For instance:

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

    def get_x(self):
        return 'x'

class Restaurant(models.Model):
    place = models.OneToOneField(Place)
    serves_pizza = models.BooleanField()

a_restaurant = Restaurant()
a_restaurant.get_x() # -> wouldn't work

This means that to obtain the name of a restaurant you can't do a_restaurant.name, you would need to follow the link: a_restaurant.place.name

Also note that when querying a Place object with the related Restaurant.

a_restaurant.save()
Place.objects.get(pk=a_restaurant.pk)  # won't work

You would have to write:

a_restaurant.save()
Place.objects.get(restaurant__pk=a_restaurant.pk)

2 and 3. are almost the same. You do get real python inheritance with these.

a_restaurant = Restaurant()
a_restaurant.get_x() # would actually work and print 'x'

Your model class Restaurant inherits everything from Place: model fields, normal instance/class attributes, managers, methods... and you can also override almost anything of these: You can't override field attributes, that's not supported.

So now you can get the values of the fields from the parent model directly:a_restaurant.name because they are inherited.

Since with these implementation a Restaurant is a also Place you can query for a Place object with Restaurant data:

a_restaurant.save()
the_place = Place.objects.get(pk=a_restaurant.pk)  
# ^ this works now and returns the equivalent `Place` instance.
the_same_restaurant = the_place.restaurant

The difference between 2 and 3 is easier to see if you give a different name to the field:

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

class Restaurant(Place):
    where = models.OneToOneField(Place, parent_link=True)
    serves_pizza = models.BooleanField()

Works exactly the same but to obtain the parent place of a Restaurant the attribute name is where:

the_place = a_restaurant.where

with 2 would have been:

the_place = a_restaurant.place_ptr

These means that place = models.OneToOneField(Place, parent_link=True) will only change the name of the link to the parent model instance. The default name is '{lowercase_model_name}_ptr'.


Last example:

With 1:

place1 = Place.objects.create(name='place_1')
place2 = Place.objects.create(name='place_2')
restaurant1 = Restaurant.objects.create(place=place1, serves_pizza=True)

print Place.objects.all() # prints [place1, place2]
print Restaurant.objects.all() # prints [restaurant1]

With 2-3:

place1 = Place.objects.create(name='place_1')
place2 = Place.objects.create(name='place_2')
restaurant1 = Restaurant.objects.create(name='place_3', serves_pizza=True)

print Place.objects.all() # prints [place1, place2, place3]
print Restaurant.objects.all() # prints [restaurant1]

Hope these helps. It grow a bit too long :/

like image 79
Adrián Avatar answered Nov 07 '22 19:11

Adrián


1 - to create restaurant you need to create place, after create restaurant, after link them, 2 - then creating restaurant, new place created and linked automaticaly, 3 - you renamed parent link to place.

Using Model Inheriting with Content Types you can list all Cafes, Restaurants, Bars, etc iterating on Place.objects.all()

like image 31
eri Avatar answered Nov 07 '22 21:11

eri