In Odoo, a Model is the backbone of every feature. It defines what data exists in the system, how it is structured, and how the business logic behaves. Every record you see in Odoo — a sales order, a customer, a product — is an instance of a model.
Understanding models is fundamental to Odoo development.
What is an Odoo Model?
A Model in Odoo represents a single business entity or concept such as a product, customer, order, or invoice. It is a Python class that defines the structure (fields) and behavior (methods) of the data.
Models correspond to database tables and encapsulate business logic. Odoo provides a powerful ORM (Object-Relational Mapping) system that allows developers to interact with the database using Python objects rather than writing raw SQL queries.
+--------------------------------------------------+
| Odoo ORM Layer |
| |
| Python Class <--> ORM <--> Database Table |
| |
| MyModel SQL my_model |
| .name INSERT name TEXT |
| .create() UPDATE ... |
| .search() SELECT |
+--------------------------------------------------+
Types of Odoo Models
Odoo provides several model base classes, each suited for different purposes:
| Model Type | Base Class | Database Table | Purpose |
|---|---|---|---|
| Basic Model | models.Model | Yes | Standard business data |
| Transient Model | models.TransientModel | Yes (temporary) | Wizards and dialogs |
| Abstract Model | models.AbstractModel | No | Shared logic/mixin |
Basic Model (`models.Model`)
This is the standard model type used for most business logic and data storage. It creates a permanent database table.
from odoo import models, fields
class MyModel(models.Model):
_name = 'my.model'
_description = 'My Model'
name = fields.Char(string='Name', required=True)
description = fields.Text(string='Description')
Model Attributes
| Attribute | Description |
|---|---|
_name | Technical name used as the database table name (dots become underscores) |
_description | Human-readable description of the model |
_inherit | Inherit from another model |
_order | Default sort order for records |
_rec_name | Field used to represent the record as a string |
Transient Model (`models.TransientModel`)
Transient models are used for temporary data, typically in wizards — dialogs that guide users through a multi-step process. Data in transient models is automatically deleted by a scheduled action after a configurable time period.
from odoo import models, fields
class MyTransientModel(models.TransientModel):
_name = 'my.transient.model'
_description = 'My Transient Model'
name = fields.Char(string='Name', required=True)
quantity = fields.Integer(string='Quantity', default=1)
Use cases:
- Import/export wizards
- Mass editing dialogs
- Confirmation prompts
- Report parameter forms
Abstract Model (`models.AbstractModel`)
Abstract models are used as base classes (mixins) that other models can inherit from. They do not create database tables. They exist solely to share common fields and methods across multiple models.
from odoo import models, fields
class MyAbstractModel(models.AbstractModel):
_name = 'my.abstract.model'
_description = 'My Abstract Model'
name = fields.Char(string='Name')
active = fields.Boolean(string='Active', default=True)
Any model that inherits from this abstract model will gain the name and active fields automatically.
Fields: Defining Data Structure
Fields define the columns in the database table and the data each record can hold. Odoo provides a rich set of field types.
Scalar Fields
| Field Type | Description | Example |
|---|---|---|
Char | Short text string | Name, reference |
Text | Long text | Description, notes |
Integer | Whole number | Quantity, count |
Float | Decimal number | Price, weight |
Boolean | True/False toggle | Is active, is published |
Date | Date only | Birth date, due date |
Datetime | Date and time | Created at, scheduled time |
Selection | Dropdown list | State, priority |
Html | Rich text / HTML | Email body, website content |
Binary | File / binary data | Attachments, images |
from odoo import models, fields
class Product(models.Model):
_name = 'product.product'
_description = 'Product'
name = fields.Char(string='Product Name', required=True)
description = fields.Text(string='Description')
price = fields.Float(string='Price', digits=(10, 2))
qty_on_hand = fields.Integer(string='Quantity on Hand')
is_active = fields.Boolean(string='Active', default=True)
date_available = fields.Date(string='Available From')
state = fields.Selection([
('draft', 'Draft'),
('available', 'Available'),
('discontinued', 'Discontinued'),
], string='State', default='draft')
Relational Fields
Relational fields define relationships between models, which correspond to foreign keys and join tables in the database.
Many2one
A many-to-one relationship. Many records of this model belong to one record of the related model.
partner_id = fields.Many2one(
'res.partner',
string='Customer',
required=True,
ondelete='restrict'
)
sale.order --> res.partner
(many) (one)
One2many
A one-to-many relationship. One record of this model has many related records in another model. This is the inverse of Many2one and requires a corresponding Many2one field in the related model.
order_line_ids = fields.One2many(
'sale.order.line',
'order_id',
string='Order Lines'
)
sale.order <-- sale.order.line
(one) (many)
Many2many
A many-to-many relationship. Many records can be associated with many records on both sides. Odoo creates a join table automatically.
tag_ids = fields.Many2many(
'product.tag',
string='Tags'
)
product.product <--> product.tag
(many) (many)
Computed Fields
A computed field calculates its value dynamically from other fields rather than storing a fixed value in the database.
from odoo import models, fields, api
class SaleOrder(models.Model):
_name = 'sale.order'
order_line_ids = fields.One2many('sale.order.line', 'order_id')
amount_total = fields.Float(
string='Total Amount',
compute='_compute_amount_total',
store=True
)
@api.depends('order_line_ids.price_subtotal')
def _compute_amount_total(self):
for order in self:
order.amount_total = sum(order.order_line_ids.mapped('price_subtotal'))
Key options:
| Option | Description |
|---|---|
compute | Name of the method that calculates the value |
store=True | Persist the computed value in the database (for searching/filtering) |
@api.depends(...) | Triggers recomputation when listed fields change |
Model Methods
Models can define methods that implement business logic.
CRUD Methods
Odoo provides built-in CRUD methods that you can override:
@api.model
def create(self, vals):
# Custom logic before creation
vals['name'] = vals.get('name', '').upper()
record = super().create(vals)
# Custom logic after creation
return record
def write(self, vals):
result = super().write(vals)
# Custom logic after update
return result
def unlink(self):
# Custom logic before deletion
for record in self:
if record.state == 'confirmed':
raise UserError("Cannot delete a confirmed record.")
return super().unlink()
Custom Business Methods
def action_confirm(self):
for record in self:
record.state = 'confirmed'
record.date_confirmed = fields.Datetime.now()
ORM Query Methods
The Odoo ORM provides powerful methods for querying data:
# Search for records
partners = self.env['res.partner'].search([
('is_company', '=', True),
('country_id.name', '=', 'Egypt')
])
# Search with limit and order
top_customers = self.env['res.partner'].search(
[('customer_rank', '>', 0)],
limit=10,
order='customer_rank desc'
)
# Search and count
count = self.env['sale.order'].search_count([('state', '=', 'sale')])
# Browse by ID
partner = self.env['res.partner'].browse(42)
# Read specific fields
data = self.env['res.partner'].search_read(
[('is_company', '=', True)],
fields=['name', 'email', 'phone']
)
The Environment (`self.env`)
Every model method has access to self.env, which provides:
| Property | Description |
|---|---|
self.env.user | The currently logged-in user |
self.env.uid | The user ID |
self.env.company | The current company |
self.env['model.name'] | Access any model by its technical name |
self.env.cr | The database cursor (for raw SQL when needed) |
Final Thoughts
Odoo models are the heart of every Odoo application. They define what data exists, how it is stored, and how the business logic operates. The ORM abstracts away raw SQL while still providing powerful querying capabilities.
Mastering models — their types, fields, computed values, and ORM methods — gives you the foundation to build any business feature in Odoo without ever writing a single SQL query directly.
Every business process in Odoo begins with a well-designed model.