Odoo ORM Guide - Exploring the Framework’s Core

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:

  1. Create – model.create({})
  2. Read – model.search(), model.browse()
  3. Update – record.write({})
  4. 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.