Profiling Slow ORM Calls in Odoo 19: Fix N+1 Queries and Improve Performance

Modern business applications depend on fast, predictable data access. In Odoo 19, the ORM provides a powerful and developer-friendly way to interact with PostgreSQL but convenience can sometimes hide serious performance costs.
One of the most common and damaging performance issues in Odoo is the N+1 query problem. This occurs when code fetches a list of records with one query and then executes an additional query for each record inside a loop. As data volume grows, response times degrade rapidly.
This guide explains how to profile slow ORM calls in Odoo 19, identify N+1 query hotspots and apply proven fixes that can reduce hundreds or thousands of database queries down to just a few.
What Is the N+1 Query Problem in Odoo?
The N+1 query problem happens when:
- One query retrieves a set of records (N)
- Each iteration over those records triggers an additional query
For example, looping through 1,000 records and accessing a relational field inside the loop can generate 1,001 database queries. This pattern is easy to introduce unintentionally and can severely impact performance as data grows.
Symptoms of Slow ORM Performance in Odoo 19
You may be facing N+1 issues if you notice:
- Pages or server actions taking seconds or minutes with larger datasets
- Logs showing repeated SELECT queries for a single view
- High PostgreSQL CPU usage while the application appears idle
- Performance degrading linearly as record count increases
A Simple Mental Test
If your code accesses a relational field inside a loop (for example, rec.partner_id.name), ask:
“Is this triggering a database query for each record?”
If the answer is yes, you are likely dealing with an N+1 issue.
How to Profile Odoo ORM Calls in Odoo 19
Odoo 19 includes built-in tools to help developers understand exactly which Python code triggers which SQL queries.
Step 1: Enable the Odoo Profiler
- Activate developer (debug) mode
- Enable the profiler for a user session or request
- Reproduce the slow action or page
The profiler records:
- SQL queries executed
- Call stacks
- Time spent per function
Step 2: Capture and Analyze Profiling Output
Profiling data can be exported as JSON or .profile files and analyzed using tools such as Speedscope or flamegraph viewers.
Look for:
- Functions generating large numbers of SQL calls
- Repeated queries with similar structure
- ORM methods executed inside loops
Step 3: Inspect SQL at the Database Level
For heavy queries:
- Enable PostgreSQL query logging
- Use EXPLAIN ANALYZE to inspect execution plans
- Identify missing indexes or inefficient joins
This combination of application profiling and database analysis provides a full picture of performance bottlenecks.
Common Causes of N+1 Queries in Odoo
The most frequent sources include:
- Accessing relational fields inside loops without batching
- Calling search() or search_count() repeatedly instead of aggregating
- Computed fields executing per-record queries
- Using browse() when only a subset of fields is needed
- On-change methods performing database queries
These patterns are easy to write but expensive at scale.
Practical Workflow: From Profiling to Fixing N+1 Issues
Use this checklist to move systematically from diagnosis to resolution:
- Reproduce the issue on a staging environment with realistic data
- Enable the Odoo profiler and capture the slow workflow
- Identify functions generating excessive SQL queries
- Locate loops that trigger ORM calls per record
- Refactor using bulk ORM methods
- Re-profile and compare query counts and response times
A successful fix often reduces query count by orders of magnitude, for example from hundreds of queries to just two or three.
Proven ORM Techniques to Eliminate N+1 Queries
Use search_read() Instead of search() + Loop
Fetch only the fields you need in a single query.
Use read() for Bulk Field Access
Avoid lazy loading relational fields one record at a time.
Use read_group() for Aggregations
Let PostgreSQL aggregate data instead of looping in Python.
Use mapped() for Batch Field Access
Odoo’s prefetching works best when fields are accessed in bulk.

Be Careful with Computed Fields
Compute values in batches and cache results where possible.
Additional Performance Levers to Combine with ORM Fixes
Fixing N+1 issues delivers the biggest wins, but performance improves further when combined with:
- Proper PostgreSQL indexing on frequently searched fields
- Avoiding heavy logic inside computed fields
- Reducing unnecessary joins
- Reviewing query plans with EXPLAIN ANALYZE
Together, these steps ensure Odoo scales predictably as data grows.
Real-World Performance Gains You Can Expect
In real projects, eliminating N+1 query paths often produces dramatic results:
- Views loading in under 1 second instead of 20–30 seconds
- Database CPU usage dropping sharply
- Faster server actions and background jobs
- Improved user experience during peak usage
These gains come from addressing root causes not from superficial caching.
Conclusion
Optimizing ORM performance in Odoo 19 is not about clever hacks, it’s about intentional data access. Fetch data in bulk, aggregate in the database and rely on the profiler to identify real bottlenecks.
Odoo 19 provides the tools needed to diagnose and fix performance issues. By profiling slow ORM calls, identifying N+1 patterns and applying bulk read techniques, developers can achieve immediate, measurable improvements.
How Wispy Helps Optimize Odoo Performance
Wispy helps teams profile, refactor and optimize Odoo 19 applications for scale. We analyze ORM usage, eliminate N+1 query patterns, tune PostgreSQL performance and refactor custom modules to meet enterprise performance standards.
Our approach focuses on measurable outcomes: faster response times, lower infrastructure load and systems that scale reliably with business growth.
FAQ’s
What is the N+1 query problem in Odoo?
It occurs when one query fetches records and additional queries are executed for each record, causing performance issues.
How can I detect N+1 queries in Odoo 19?
Use Odoo’s built-in profiler to track SQL queries and identify repeated database calls inside loops.
Which ORM methods help fix N+1 issues?
Methods like search_read(), read(), read_group() and mapped() help batch data access and reduce queries.
Should I store raw SQL instead of using the ORM?
No. The ORM is efficient when used correctly; issues arise from incorrect access patterns, not the ORM itself.
How much performance improvement can fixing N+1 provide?
Fixing N+1 queries often reduces response time from tens of seconds to under one second.