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

后端 未结 2 2085
礼貌的吻别
礼貌的吻别 2021-02-13 04:43

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

2条回答
  •  后悔当初
    2021-02-13 05:31

    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 :/

提交回复
热议问题