Odoo meta programming refers to the mechanisms by which models, fields, methods, and behaviors are constructed, merged, and modified at load time, not at runtime in the traditional Python sense.
What looks like ordinary Python classes is, in reality, a model definition language processed by Odoo’s registry and ORM engine.
1. Model Classes Are Not Instantiated Normally
In standard Python, a class defines how objects are instantiated.
In Odoo, a model class is parsed, registered, and merged into the registry.
class SaleOrder(models.Model):
_name = 'sale.order'
This class is never instantiated directly.
Odoo reads the class body, extracts metadata, and builds a runtime model dynamically.
2. _name and _inherit Are Meta Instructions
These attributes are not configuration values; they are directives to the model loader.
Create a new model
_name = 'custom.model'
Extend an existing model
_inherit = 'sale.order'
Multiple inheritance (mixin-style)
_inherit = ['sale.order', 'mail.thread']
Odoo merges:
- fields
- methods
- constraints
- metadata
into a single runtime model, regardless of how many Python classes define it.
3. Method Resolution Is Registry-Based, Not Class-Based
Python’s MRO (Method Resolution Order) is secondary.
Odoo resolves methods after all modules are loaded, based on:
- module dependency order
- inheritance chains
- override definitions
def action_confirm(self):
res = super().action_confirm()
self.your_custom_function()
return res
super() does not mean “parent class” in a simple sense.
It means “previous method version in the registry chain”.
4. Fields Are Collected Declaratively
Fields are not attributes on instances.
They are descriptors collected during class parsing.
amount_total = fields.Monetary(string='Amount Total')
approved_by = fields.Many2one('res.users', string='Approved By')
At load time, Odoo:
- registers the field
- attaches it to the model schema
- links compute, inverse, and search logic
- decides storage behavior
No field exists until the registry is built.
5. Decorators Modify the Model Graph
Decorators like @api.depends do not wrap functions in the Python sense.
They annotate metadata used later by the ORM.
@api.depends('line_ids.price_subtotal')
def _compute_amount_total(self):
for record in self:
record.amount_total = sum(record.line_ids.mapped('price_subtotal'))
Odoo extracts:
- dependency graph
- recomputation triggers
- store vs non-store behavior
The decorator participates in graph construction, not execution flow.
6. Constraints and SQL Rules Are Meta-Level Declarations
_sql_constraints = [
('uniq_code', 'unique(code)', 'Code must be unique')
]
This is not runtime validation logic.
It instructs Odoo to:
- generate SQL constraints
- apply them during module installation or upgrade
The Python class acts as a schema definition, not business logic.
7. Views and XML Participate in Meta Programming
XML files are not static UI definitions.
They are instructions to mutate existing views.
<xpath expr="//field[@name='partner_id']" position="after">
<field name="custom_field"/>
</xpath>
At load time:
- views are parsed
- inheritance trees are resolved
- final XML is generated
The result is a synthesized view, not the original file.
