90 lines
3.3 KiB
Markdown
90 lines
3.3 KiB
Markdown
---
|
|
name: odoo-orm-expert
|
|
description: "Master Odoo ORM patterns: search, browse, create, write, domain filters, computed fields, and performance-safe query techniques."
|
|
risk: safe
|
|
source: "self"
|
|
---
|
|
|
|
# Odoo ORM Expert
|
|
|
|
## Overview
|
|
|
|
This skill teaches you Odoo's Object Relational Mapper (ORM) in depth. It covers reading/writing records, building domain filters, working with relational fields, and avoiding common performance pitfalls like N+1 queries.
|
|
|
|
## When to Use This Skill
|
|
|
|
- Writing `search()`, `browse()`, `create()`, `write()`, or `unlink()` calls.
|
|
- Building complex domain filters for views or server actions.
|
|
- Implementing computed, stored, and related fields.
|
|
- Debugging slow queries or optimizing bulk operations.
|
|
|
|
## How It Works
|
|
|
|
1. **Activate**: Mention `@odoo-orm-expert` and describe what data operation you need.
|
|
2. **Get Code**: Receive correct, idiomatic Odoo ORM code with explanations.
|
|
3. **Optimize**: Ask for performance review on existing ORM code.
|
|
|
|
## Examples
|
|
|
|
### Example 1: Search with Domain Filters
|
|
|
|
```python
|
|
# Find all confirmed sale orders for a specific customer, created this year
|
|
import datetime
|
|
|
|
start_of_year = datetime.date.today().replace(month=1, day=1).strftime('%Y-%m-%d')
|
|
|
|
orders = self.env['sale.order'].search([
|
|
('partner_id', '=', partner_id),
|
|
('state', '=', 'sale'),
|
|
('date_order', '>=', start_of_year),
|
|
], order='date_order desc', limit=50)
|
|
|
|
# Note: pass dates as 'YYYY-MM-DD' strings in domains,
|
|
# NOT as fields.Date objects — the ORM serializes them correctly.
|
|
```
|
|
|
|
### Example 2: Computed Field
|
|
|
|
```python
|
|
total_order_count = fields.Integer(
|
|
string='Total Orders',
|
|
compute='_compute_total_order_count',
|
|
store=True
|
|
)
|
|
|
|
@api.depends('sale_order_ids')
|
|
def _compute_total_order_count(self):
|
|
for record in self:
|
|
record.total_order_count = len(record.sale_order_ids)
|
|
```
|
|
|
|
### Example 3: Safe Bulk Write (avoid N+1)
|
|
|
|
```python
|
|
# ✅ GOOD: One query for all records
|
|
partners = self.env['res.partner'].search([('country_id', '=', False)])
|
|
partners.write({'country_id': self.env.ref('base.us').id})
|
|
|
|
# ❌ BAD: Triggers a separate query per record
|
|
for partner in partners:
|
|
partner.country_id = self.env.ref('base.us').id
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
- ✅ **Do:** Use `mapped()`, `filtered()`, and `sorted()` on recordsets instead of Python loops.
|
|
- ✅ **Do:** Use `sudo()` sparingly and only when you understand the security implications.
|
|
- ✅ **Do:** Prefer `search_count()` over `len(search(...))` when you only need a count.
|
|
- ✅ **Do:** Use `with_context(...)` to pass context values cleanly rather than modifying `self.env.context` directly.
|
|
- ❌ **Don't:** Call `search()` inside a loop — this is the #1 Odoo performance killer.
|
|
- ❌ **Don't:** Use raw SQL unless absolutely necessary; use ORM for all standard operations.
|
|
- ❌ **Don't:** Pass Python `datetime`/`date` objects directly into domain tuples — always stringify them as `'YYYY-MM-DD'`.
|
|
|
|
## Limitations
|
|
|
|
- Does not cover **`cr.execute()` raw SQL** patterns in depth — use the Odoo performance tuner skill for SQL-level optimization.
|
|
- **Stored computed fields** can cause significant write overhead at scale; this skill does not cover partitioning strategies.
|
|
- Does not cover **transient models** (`models.TransientModel`) or wizard patterns.
|
|
- ORM behavior can differ slightly between Odoo SaaS and On-Premise due to config overrides.
|