Odoo Inheritance for the uninitiated

Complex technology can look like magic. And when you use it long enough, magic can become second nature, it can become just “the way it works”. But I don’t like magic when it comes to computers. Specially in a system built with Python, whose Zen includes Explicit is better than implicit.

I’m barely starting with Odoo (formerly OpenERP) and one of the bits of magic that struck me most was its inheritance mechanisms. I read the docs, talked to my senior colleagues and browsed the code heavily. And I wanted to document my findings, from the point of view of a beginner, before the magic became “the way it works”.

Three Inheritance Modes

Odoo provides three ways to inherit from and extend existing models. We’ll check them one by one and, to dispel the magic, we’ll see what’s going on behind the scenes.

First, let’s see what’s common to all inheritance modes. To define new models you inherit, in the standard Python sense, from Odoo’s models.Model, like this:

class Polygon(models.Model):
    _name = 'shapes.polygon'

    pos_x = fields.Float(default=0)
    pos_y = fields.Float(default=0)

Models become tables in Odoo’s database. The _name attribute is how your model will be identified across Odoo, and it’s where Odoo derives the table name from. After you install a module including the model above, you’ll see the following in pgAdmin:

Note how pos_x and pos_y appear as columns in a table shapes_polygon and how Odoo created five extra fields automatically.

Mode One: Classical Inheritance

The first way to extend a model in Odoo is similar, in spirit, to prototype inheritance in other programming languages. You want to define a new model, but you want to reuse the fields and methods of an existing model. You achieve that in Odoo like this:

class Rectangle(models.Model):
    _name = 'shapes.rectangle'
    _inherit = 'shapes.polygon'

    width = fields.Float(default=0)
    height = fields.Float(default=0)

The key attribute here is _inherit. Here you specify the existing model that you want to use as prototype for your new model. Your model gets all the fields of its base model, and it can even use, redefine and access the base methods through super().

With this inheritance, Odoo creates a new table in the database, and includes all the columns of the inherited model. It looks like this in pgAdmin:

Mode Two: Extension

In this mode, you don’t create a new model. You want to extend an existing model in place. This makes it possible to not only add fields and methods, but to redefine existing fields and methods. In this way, your changes may have effect on all current users of the model. This is used very frequently by Odoo’s and third parties’ modules.

The syntax is actually quite simple: include an _inherit attribute but omit _name, like this:

class DescriptivePolygon(models.Model):
    _inherit = 'shapes.polygon'

    description = fields.Char()
    pos_x = fields.Float(string="X", default=0)
    pos_y = fields.Float(string="Y", default=0)