Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More

Multi-table inheritance (MTI)

This document describes the lino_book.projets.mti demo project, an application used for testing and explaining Multi-table inheritance.

The example database

Here is the models.py file used for this example. This is classical Django know-how: Restaurant inherits from Place, and Place is not abstract. That’s what Django calls multi table inheritance.

# Copyright 2010-2020 Rumma & Ko Ltd
# License: GNU Affero General Public License v3 (see file COPYING for details)

from lino.api import dd
from django.db import models
from lino.mixins.polymorphic import Polymorphic


class Person(models.Model):

    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name


class Place(Polymorphic):

    name = models.CharField(max_length=50)
    owners = models.ManyToManyField(Person)

    def __str__(self):
        if self.pk is None:
            return self.name
        return "%s (owners=%s)" % (self.name, ', '.join(
            [str(o) for o in self.owners.all()]))


class Restaurant(Place):

    serves_hot_dogs = models.BooleanField(default=False)
    cooks = models.ManyToManyField(Person)

    def __str__(self):
        if self.pk is None:
            return self.name
        return "%s (owners=%s, cooks=%s)" % (self.name, ', '.join([
            str(o) for o in self.owners.all()
        ]), ', '.join([str(o) for o in self.cooks.all()]))


class Visit(models.Model):

    allow_cascaded_delete = ['place']
    person = dd.ForeignKey(Person)
    place = dd.ForeignKey(Place)
    purpose = models.CharField(max_length=50)

    def __str__(self):
        return "%s visit by %s at %s" % (self.purpose, self.person,
                                         self.place.name)


class Meal(models.Model):

    allow_cascaded_delete = ['restaurant']
    person = dd.ForeignKey(Person)
    restaurant = dd.ForeignKey(Restaurant)
    what = models.CharField(max_length=50)

    def __str__(self):
        return "%s eats %s at %s" % (self.person, self.what,
                                     self.restaurant.name)


from .ui import *
>>> rt.show("app.Persons")
========
 name
--------
 Anne
 Bert
 Claude
 Dirk
 Ernie
 Fred
========
>>> rt.show("app.Places")
==== ===================== ============================
 ID   name                  owners
---- --------------------- ----------------------------
 1    Bert's pub            `Anne <…>`__, `Bert <…>`__
 2    The Chopping Shack    `Anne <…>`__
 3    The Abacus Well       `Claude <…>`__
 4    The Olive Lounge      `Ernie <…>`__
 5    The Autumn Bite       `Anne <…>`__
 6    The Private Mission   `Claude <…>`__
 7    Nova                  `Ernie <…>`__
 8    Babylon               `Anne <…>`__
 9    Blossoms              `Claude <…>`__
 10   Whisperwind           `Ernie <…>`__
 11   Catch                 `Anne <…>`__
==== ===================== ============================
>>> rt.show("app.Restaurants")
==== ===================== ================= ================ ==============
 ID   name                  serves hot dogs   owners           cooks
---- --------------------- ----------------- ---------------- --------------
 2    The Chopping Shack    No                `Anne <…>`__     `Bert <…>`__
 3    The Abacus Well       No                `Claude <…>`__   `Dirk <…>`__
 4    The Olive Lounge      No                `Ernie <…>`__    `Fred <…>`__
 5    The Autumn Bite       No                `Anne <…>`__     `Bert <…>`__
 6    The Private Mission   No                `Claude <…>`__   `Dirk <…>`__
 7    Nova                  No                `Ernie <…>`__    `Fred <…>`__
 8    Babylon               No                `Anne <…>`__     `Bert <…>`__
 9    Blossoms              No                `Claude <…>`__   `Dirk <…>`__
 10   Whisperwind           No                `Ernie <…>`__    `Fred <…>`__
 11   Catch                 No                `Anne <…>`__     `Bert <…>`__
==== ===================== ================= ================ ==============