Introduction
At the heart of every Odoo app is its ORM (Object-Relational Mapping)—a powerful system that lets you manipulate database records like Python objects. If you’ve ever wondered how a simple create() call turns into structured SQL magic, this guide is for you.
We’ll break down how Odoo ORM works, why it matters, and how to master it like a seasoned developer.
What Is an ORM and Why Does Odoo Use One?
ORM stands for Object-Relational Mapping. It allows developers to interact with the database using Python classes instead of raw SQL.
In Odoo, the ORM:
- Maps database tables to Python classes
- Simplifies CRUD operations
- Maintains consistency across models
Note: If you’re also working on the customer side of Odoo, don’t miss our guide on Odoo 18 CRM: Elevating Customer Relationships.
Key Components of the Odoo ORM
- Model: Your Python class (e.g., res.partner)
- Field: Columns in the table (e.g., name, email)
- Recordset: Collections of records, returned from ORM methods
- Environment (env): Context, cache, and access rights bundled into one
The Lifecycle of a Record
Every Odoo record goes through these phases:
- Create – model.create({})
- Read – model.search(), model.browse()
- Update – record.write({})
- Delete – record.unlink()
How to Create Records in Odoo ORM
self.env[‘res.partner’].create({
‘name’: ‘Tayyab Enterprises’,
’email’: ‘info@tayyab.com’
})
Pro Tip: Use sudo() cautiously—it bypasses access rights.
How to Read Records
partners = self.env[‘res.partner’].search([(‘is_company’, ‘=’, True)])
Use read(), browse(), or search() depending on your use case.
How to Update Records
partner = self.env[‘res.partner’].browse(45)
partner.write({’email’: ‘new@email.com’})
Always validate user permissions before sensitive updates.
How to Delete Records
partner.unlink()
Use unlink() with caution—it’s irreversible.
Exploring Decorators: @api.model, @api.depends, @api.onchange
Odoo uses decorators to add functionality to ORM methods:
- @api.model for methods on the model class
- @api.depends to trigger recomputations
- @api.onchange to update values in forms dynamically
Working with Related Fields
company_country = fields.Char(related=’company_id.country_id.name’)
These fields avoid data duplication while giving contextual information.
Custom Constraints with @api.constrains
@api.constrains(’email’)
def _check_email(self):
if not self.email.endswith(‘@gmail.com’):
raise ValidationError(‘Only Gmail addresses are allowed’)
Performance Tips for Using ORM Efficiently
- Minimize loops with batched operations
- Use mapped() and filtered() for clean code
- Avoid unnecessary .search() in loops
- Always enable logging during testing
When Not to Use the ORM
Sometimes raw SQL is necessary:
- Complex queries with joins across multiple models
- Real-time data aggregation
Use self.env.cr.execute() carefully.
Conclusion
The ORM is more than just a helper—it’s the engine of Odoo development. Mastering it means writing cleaner, safer, and faster code.
Next time you write self.env[‘model’].create(), know that you’re wielding one of Odoo’s most powerful tools.
Need a custom Odoo solution built on a deep understanding of ORM? Book a free consultation with our experts and turn your business logic into powerful, scalable code.
Frequently Asked Questions
1. What’s the difference between search() and browse()?
search() queries the DB; browse() is used when you already know the record ID.
2. Can I override create() or write() methods?
Yes—use them to add custom logic during record operations.
3. What is a Recordset in Odoo?
It’s a set of records returned by ORM methods—like a Python list of objects.
4. Are ORM operations atomic?
Yes, they’re executed in transactions and roll back on errors.
5. Can I log SQL queries in Odoo?
Yes—use –log-level=debug_sql when running the server.