Three Types of Inheritance
Odoo has three distinct inheritance mechanisms, and confusing them is one of the most common mistakes for new developers.
| Type | Declaration | New Table? | Use Case |
|---|---|---|---|
| Classical | _inherit = 'model' | No | Extend existing model (add fields, override methods) |
| Prototype | _inherit = 'model' + _name = 'new.model' | Yes | Copy model structure to a new model |
| Delegation | _inherits = {'model': 'field_id'} | Yes | Compose models (like has-a relationship) |
Classical Inheritance (Most Common)
Extends an existing model without creating a new database table. Used in 95% of customizations.
# Add a field to res.partner
class ResPartner(models.Model):
_inherit = 'res.partner'
credit_score = fields.Integer(string='Credit Score')
loyalty_tier = fields.Selection([
('bronze', 'Bronze'),
('silver', 'Silver'),
('gold', 'Gold'),
], string='Loyalty Tier', default='bronze')
def action_check_credit(self):
"""New method added to res.partner."""
for partner in self:
# Custom logic
passOverriding Methods
class SaleOrder(models.Model):
_inherit = 'sale.order'
def action_confirm(self):
"""Override confirm to add custom logic."""
# Call original method first
result = super().action_confirm()
# Add custom logic after
for order in self:
if order.amount_total > 10000:
order.message_post(
body="Large order confirmed: %s" % order.amount_total
)
return resultAlways call super() unless you intentionally want to replace the entire method.
Prototype Inheritance (Rare)
Creates a new model that copies the fields and methods of an existing model into a new database table.
# Create a new model based on res.partner structure
class Vendor(models.Model):
_name = 'my.vendor'
_inherit = 'res.partner' # Copy structure
_description = 'Vendor'
# This creates a NEW table 'my_vendor' with all res.partner fields
# Plus any new fields you add:
vendor_rating = fields.Float(string='Rating')
payment_terms = fields.Char(string='Payment Terms')Use sparingly. Prototype inheritance duplicates data and does not maintain a link to the original model. In most cases, classical inheritance with a type field is better.
Delegation Inheritance
Composes models using a foreign key. The child model has access to all parent fields through the link field. Think of it as a "has-a" relationship.
class Employee(models.Model):
_name = 'hr.employee'
_inherits = {'res.partner': 'address_home_id'}
_description = 'Employee'
# address_home_id is the link to res.partner
address_home_id = fields.Many2one(
'res.partner', string='Private Address',
required=True, ondelete='restrict',
)
# Employee-specific fields
department_id = fields.Many2one('hr.department')
job_title = fields.Char()
# Can access partner fields directly:
# employee.name → reads from res.partner
# employee.email → reads from res.partnerDelegation creates a new table but links to the parent via a foreign key. Reading partner fields on the employee transparently reads from the partner record.
View Inheritance
<!-- Extend an existing view -->
<record id="view_partner_form_inherit" model="ir.ui.view">
<field name="name">res.partner.form.inherit</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='phone']" position="after">
<field name="credit_score"/>
<field name="loyalty_tier"/>
</xpath>
</field>
</record>Mixin Pattern
Abstract models used as mixins provide reusable functionality:
class TrackingMixin(models.AbstractModel):
_name = 'tracking.mixin'
_description = 'Tracking Mixin'
created_by = fields.Many2one('res.users', default=lambda self: self.env.user)
last_modified_by = fields.Many2one('res.users')
def write(self, vals):
vals['last_modified_by'] = self.env.user.id
return super().write(vals)
# Use the mixin
class Equipment(models.Model):
_name = 'equipment.item'
_inherit = ['mail.thread', 'tracking.mixin'] # Multiple inheritance
_description = 'Equipment'
name = fields.Char(required=True)Common Mistakes
- Forgetting
super()in method overrides — breaks the original behavior - Using prototype inheritance when classical is needed — creates unnecessary data duplication
- Not specifying
_namefor prototype — accidentally extends the original model instead of creating a new one - Circular inheritance — Model A inherits B which inherits A
- Overriding
create/writewithoutsuper()— breaks ORM functionality
Getting Started
Use Claude Code to generate inheritance code — it understands when to use classical vs prototype vs delegation based on your requirements. Deploy and test on DeployMonkey.