Odoo Development
Odoo Model Inheritance: Extension, Prototype and Delegation
Odoo has three inheritance mechanisms that look similar and behave nothing alike. One reopens a model, one copies it, one links to it. Choosing wrong is not a style mistake, it is a decision about where your data physically lives, and it is expensive to reverse once records exist.
Part of our guide to Odoo Development
Three mechanisms, three different tables
The choice determines where data physically lives, which is why it is hard to undo later.
Classic extension modifies the existing table. Prototype creates a new one. Delegation creates a new one holding a foreign key. Everything else follows from that.
Extension: reopening a model
_inherit with no _name. Your class reopens the existing model and everything you add lands on the same table, visible to every other module using it.
from odoo import fields, models
class ResPartner(models.Model):
_inherit = "res.partner"
loyalty_tier = fields.Selection([
("bronze", "Bronze"),
("gold", "Gold"),
], default="bronze")Every existing partner now has a loyalty tier. No new model, no new table, no migration of existing records.
This is the right answer roughly nine times out of ten. If you want existing records to gain a field or a behaviour, this is the mechanism.
Prototype: copying the structure
_inherit plus a different _name. You get a new model with its own table that starts as a copy of the parent's structure.
class EquipmentTemplate(models.Model):
_name = "equipment.template"
_inherit = "maintenance.equipment"
_description = "Equipment Template"The important consequence: the two are now separate. Records do not move between them, changes Odoo makes to the parent's data do not reach yours, and a field added to the parent later does not appear here unless the class is reloaded.
Genuinely useful occasionally. Chosen by mistake often, usually by someone who wanted extension and got a second table quietly containing nothing.
Delegation: linking to a parent record
_inherits, with the s, is a different mechanism that happens to share most of a name. It maps parent models to the Many2one fields that point at them:
class VehicleAsset(models.Model):
_name = "vehicle.asset"
_description = "Vehicle Asset"
_inherits = {"fleet.vehicle": "vehicle_id"}
vehicle_id = fields.Many2one("fleet.vehicle", required=True, ondelete="cascade")
asset_value = fields.Monetary()Your model now exposes every field of fleet.vehicle as though it owned them, while storing none of them. Reads and writes pass through to the linked record. This is how a product variant relates to its template.
Two things to know before choosing it. Creating one of your records creates a parent record too, so the parent's lifecycle is now partly yours. And if two delegated parents define the same field name, the one that wins is the last in the dictionary, which is a quiet way to get the wrong value.
Use it when your object genuinely *is a* parent object with extra data. Reach for a plain Many2one when it merely *relates to* one.
Overriding methods
Extension usually means overriding behaviour as well as adding fields, and the rule is to wrap rather than replace:
class SaleOrder(models.Model):
_inherit = "sale.order"
def action_confirm(self):
for order in self:
if order.amount_total > 50000 and not order.approval_id:
raise UserError("Orders above 50,000 need approval first.")
return super().action_confirm()The check runs, then the original behaviour runs unchanged, including whatever any other installed module added to the same method.
An override that reimplements the parent instead of calling super() is a standing commitment to re-read the parent's source at every upgrade. Nothing warns you when the parent gains a step that yours does not perform. The invoice is simply not created, or the stock is not reserved, and it surfaces weeks later. This is one of the most expensive habits in Odoo code, and it is covered further in what actually breaks.
Two practical notes. Do your work before super() when you are validating, and after when you need the result. And when you need to change the arguments rather than the outcome, change them and pass them on rather than reimplementing the body.
Choosing, in one pass
- Should existing records gain this? Extension.
- Is this a genuinely separate object that merely resembles an existing one? Prototype.
- Is this an existing object plus extra data, sharing its identity? Delegation.
- Does it just point at another record? A plain Many2one, no inheritance at all.
That last option is missing from most explanations and is frequently the right answer. Inheritance is not the only way to relate two models, and a Many2one carries none of the lifecycle coupling that delegation does.
The view side
Views have their own inheritance, and the same principle governs it: extend, do not replace. A view you copied wholesale stops receiving everything Odoo changes about it, silently, which is the visual equivalent of an override that skipped super(). Anchor an XPath on something structural and stable rather than on a position, and the upgrade will tell you honestly when your anchor is gone.
FAQ
Questions, answered.
What is the difference between _inherit and _inherits in Odoo?
_inherit with no _name reopens an existing model in place, adding to the same table. _inherits, with the s, is delegation: a dictionary mapping a parent model to a Many2one field, where your model exposes the parent's fields but stores none of them. They are unrelated mechanisms with confusingly similar names.
Do I always have to call super() when overriding an Odoo method?
Practically always. Skipping it discards the parent implementation, including anything Odoo or another installed module added to that method. The behaviour you replaced does not error, it just stops happening, and the next version's additions never run either.
How do I add a field to an existing Odoo model like res.partner?
Declare a class with _inherit set to that model and no _name, then declare the field as a normal class attribute. Odoo adds a column to the existing table. You do not redeclare the model's other fields.
Keep reading
More on Odoo Development.
The full guide, plus the other articles in this cluster.
With CODEerts
Need something built properly?
We are certified Odoo partners. We build custom Odoo modules that extend the framework rather than fight it, and we publish our own apps on the Odoo Store.
See how we can helpBook a callReady to make Odoo work the way your business does?
Book a free callCODEerts is a team of certified Odoo partners and full-stack engineers. We implement, customise and support Odoo ERP, then build the software around it.