When working with Odoo’s ORM, most developers are comfortable using search(), read(), or the convenient search_read() method.
But when it comes to reporting, dashboards, KPIs, and performance optimization, another method becomes extremely important — read_group().
Many performance issues in production systems happen simply because the wrong ORM method was used.
In this article, we’ll clearly understand:
- What search_read() really does
- What read_group() is designed for
- When to use each
- And why choosing correctly matters for scalability
1. What is search_read()?
search_read() is essentially a shortcut that combines:
search() + read()
It searches records based on a domain and immediately returns selected fields as a list of dictionaries.
Example
students = self.env["student.student"].search_read(
domain=[("active", "=", True)],
fields=["name", "email", "phone"]
)
print(students)
Output
A list of dictionaries:
[
{"name": "John", "email": "john@email.com", "phone": "123456"},
...
]
When to Use search_read()
- Fetching records for APIs
- Displaying list/form data
- Lightweight data retrieval
- Exporting record details
- Small to medium datasets
It’s simple, readable, and perfect for record-level operations.
2. Limitations of search_read()
Where developers go wrong is trying to use search_read() for reporting.
It has important limitations:
- ❌ No aggregation support (sum, avg, count, etc.)
- ❌ No grouping capability
- ❌ Loads all matching records into memory
- ❌ Not scalable for large datasets
Real-World Example
Imagine you need:
Total fees collected per class
Using search_read():
- Fetch all students
- Loop in Python
- Manually calculate totals
That means:
- More memory usage
- More Python processing
- Slower performance on large datasets
This becomes a serious issue in production systems with thousands or millions of records.
3. Introducing read_group()
read_group() is designed specifically for aggregation and grouping.
Think of it as:
SELECT class_id, SUM(fees)
FROM student_student
GROUP BY class_id;
But done using Odoo ORM.
Example: Total Fees by Class
result = self.env["student.student"].read_group(
domain=[],
fields=["fees:sum"],
groupby=["class_id"]
)
print(result)
Output Contains:
- class_id
- fees_sum
- __count (number of records in that group)
Everything is calculated directly inside PostgreSQL.
No Python loops.
No unnecessary memory load.
4. Multiple Aggregations in One Query
One of the powerful features of read_group() is that you can aggregate multiple values at once.
result = self.env["sale.order"].read_group(
domain=[("state", "=", "sale")],
fields=[
"amount_total:sum",
"amount_total:avg"
],
groupby=["user_id"]
)
This returns:
- Total sales per salesperson
- Average order value
- Record count
All computed at database level in a single query.
This is extremely efficient.
5. Date-Based Grouping (Very Powerful for Dashboards)
read_group() supports time-based grouping.
result = self.env["sale.order"].read_group(
domain=[],
fields=["amount_total:sum"],
groupby=["date_order:month"]
)
Supported time granularities:
- day
- week
- month
- quarter
- year
This is exactly how Odoo builds:
- Pivot views
- Graph views
- Dashboard analytics
If you're building KPIs or management reports — this is the correct tool.
6. Performance Comparison (Real Scenario)
Let’s assume:
10,000 confirmed sale orders.
❌ Using search_read():
- Fetch 10,000 records
- Loop in Python
- Calculate totals
- High memory usage
- Slower execution
✅ Using read_group():
- Database calculates aggregation
- Only grouped results returned
- Minimal data transfer
- Faster and scalable
The key difference:
search_read() moves data to Python to calculate
read_group() lets PostgreSQL do the calculation
Database-level aggregation will always scale better.
7. Using Both Together (Real Production Pattern)
In real-world systems, both methods are used together.
Example:
- Use read_group() → Get grouped analytics (Sales per month)
- Use search_read() → Fetch detailed records when user drills down
This is exactly how smart dashboards and reporting modules are built in Odoo.
Understanding this pattern separates average Odoo developers from performance-conscious ones.
Key Differences Summary
| Feature | search_read() | read_group() |
|---|---|---|
| Record Retrieval | ✅ Yes | ❌ No |
| Aggregation (sum, avg, count) | ❌ No | ✅ Yes |
| Grouping | ❌ No | ✅ Yes |
| Suitable for Dashboards | ❌ No | ✅ Yes |
| Scalable for Large Data | ⚠️ Limited | ✅ Yes |
Both methods are important — but they solve different problems.
Use:
- search_read() → For retrieving record details
- read_group() → For reports, KPIs, and analytics
If you care about performance in large-scale Odoo deployments, always prefer database-level aggregation over Python loops.
Mastering these two methods will help you build scalable dashboards, efficient APIs, and high-performance reporting systems — which is critical in enterprise Odoo environments.