Why Three Types?
Odoo has three inheritance mechanisms, each serving a different purpose. Confusing them is one of the most common mistakes new developers make. Here is exactly when and how to use each.
Type 1: Extension Inheritance (Most Common)
Add fields or methods to an existing model without creating a new table.
class ResPartner(models.Model):
_inherit = 'res.partner' # Extend existing model
# NO _name — same model, same table
loyalty_points = fields.Integer(string='Loyalty Points', default=0)
membership_tier = fields.Selection([
('bronze', 'Bronze'),
('silver', 'Silver'),
('gold', 'Gold'),
], string='Tier')
def compute_tier(self):
for partner in self:
if partner.loyalty_points >= 1000:
partner.membership_tier = 'gold'
elif partner.loyalty_points >= 500:
partner.membership_tier = 'silver'
else:
partner.membership_tier = 'bronze'What Happens
- No new database table — columns added to
res_partner - All existing res.partner records gain the new fields
- Existing methods still work
- You can override existing methods (call
super())
Use When
You want to add fields or behavior to an existing Odoo model. This is 90% of customization work.
Type 2: Delegation Inheritance (_inherits)
Create a new model that automatically includes all fields from another model via a required Many2one link.
class Student(models.Model):
_name = 'school.student'
_inherits = {'res.partner': 'partner_id'} # Delegation
partner_id = fields.Many2one(
'res.partner', required=True, ondelete='cascade',
)
student_id = fields.Char(string='Student ID')
grade = fields.Selection([...])
enrollment_date = fields.Date()What Happens
- New table:
school_student(with student-specific fields) - Each student record has a linked
res.partnerrecord - When you read
student.name, it reads from the linked partner - When you write
student.name = 'Alice', it writes to the partner - Creating a student auto-creates a partner (or links existing)
Use When
You want a new model that "is a" special version of an existing model. Student is a partner. Employee is a partner. The new model has all partner fields plus its own fields.
Standard Odoo Examples
# res.users _inherits res.partner
# → Every user IS a partner (has name, email, phone)
# → User adds: login, password, groups
# product.product _inherits product.template
# → Every variant IS a template (has name, price)
# → Variant adds: specific attributes, barcodeType 3: Prototype Inheritance (_inherit + _name)
Create a completely new model that copies the structure of an existing model.
class MailBlacklist(models.Model):
_name = 'my.blacklist'
_inherit = 'mail.thread' # Copy structure, NOT extend
_description = 'My Blacklist'
# Gets all fields and methods from mail.thread
# but in a completely separate table: my_blacklistWhat Happens
- New table:
my_blacklist - All fields and methods from
mail.threadare copied - Changes to your model do NOT affect
mail.thread - Changes to
mail.threaddo NOT affect your model
Use When
You want to create a new model with the same structure as an existing one. Rare in practice — mostly used with abstract models like mail.thread and mail.activity.mixin.
Abstract Models
class MailThread(models.AbstractModel):
_name = 'mail.thread'
# AbstractModel — no database table
# Designed to be inherited via prototype inheritance
class SaleOrder(models.Model):
_name = 'sale.order'
_inherit = ['mail.thread', 'mail.activity.mixin']
# Gets chatter and activities — these are prototype inherits
# from abstract modelsComparison Table
| Aspect | Extension | Delegation | Prototype |
|---|---|---|---|
| Syntax | _inherit = 'model' | _inherits = {'model': 'field'} | _name = 'new' |
| New table? | No | Yes | Yes |
| New model? | No | Yes | Yes |
| Modifies original? | Yes | No | No |
| Frequency | 90% of cases | Rare | With abstract models |
Common Mistakes
- Adding _name when you meant to extend — Creates a new model instead of extending the existing one
- Using _inherits when _inherit is enough — Unnecessary complexity and extra table
- Forgetting super() in method overrides — Breaks the original method's behavior
- Multiple _inherit (list) —
_inherit = ['mail.thread', 'mail.activity.mixin']is prototype inherit from multiple abstract models, not extension of multiple models