It’s rare for issues with Odoo performance to manifest in a surprising way. Everything works perfectly in development, slightly poorly in testing, and catastrophically under load. In nearly all cases, the problem turns out to be unnecessary queries made against the database, fetching the same results repeatedly, even when they hardly ever change.
The Odoo team provides a robust caching framework built into Odoo itself, but rarely uses more than the tip of its functionality. In this guide, we’ll explore everything about Odoo caching from basic ORM caches to advanced shared Redis caches, empowering you to make your application perform like a charm.
What Is Caching in Odoo?
In simple terms, caching refers to saving the output of an expensive computation for reuse. This implies that, rather than having to access the database for each request for the same information, the computation is done only once, and then the information is served from cache.
In Odoo, caching is used most heavily for:
- Avoiding repeated database queries for static or rarely-changing data
- Speeding up access to system configuration parameters
- Reducing computation time for complex business logic
- Caching responses from external APIs
The Odoo cache exists within the odoo/tools/cache.py file and is at the process level. This implies that each Odoo process will have its own cache, and the cache is independent of other caches for other processes.
Understanding @tools.ormcache
The ormcache function serves as Odoo’s principal method for implementing cache. The ormcache function will cache the results that are produced by any method, as determined by the parameters used in the call.
Basic Usage
Here's a straightforward example — caching a country lookup by ISO code:
from odoo import tools
class ResCountry(models.Model):
_name = 'res.country'
@tools.ormcache('code')
def _get_country_by_code(self, code):
return self.search([('code', '=', code)], limit=1).id
Once this method is called with a particular code value, the result is stored in cache. Every subsequent call with that same code returns the cached ID instantly — no database query.
Variants of ormcache
Odoo gives you a few flavours of ormcache depending on your specific situation. Here's when to use each one.
@tools.ormcache(*args)
The standard decorator. Caches based on the argument names you provide.
@tools.ormcache('self.env.uid', 'model_name')
def _get_access_rights(self, model_name):
# Result is cached per user and per model
...@tools.ormcache_context(*args, keys=())
It functions similarly to the decorator pattern but also takes into account particular context keys. If your method produces different outcomes based on the user’s locale or active company, then you can use this design pattern.
@tools.ormcache('product_id', 'self.env.context.get("lang")')
def _get_product_name(self, product_id):
return self.browse(product_id).with_context(
lang=self.env.context.get('lang')).nameHow Cache Invalidation Works
One of the nicest things about ormcache is that you mostly don't have to think about invalidation — the ORM handles it for you. Any write operation on a model automatically clears the relevant cache entries.
| Event | Cache Cleared? |
| create() called on the model | Yes |
| write() called on the model | Yes |
| unlink() called on the model | Yes |
| Odoo worker process restarts | Yes |
| Manual clear_caches() call | Yes |
If you ever need to force a cache clear manually — say, after a migration script or a direct database update — you can do it like this:
# Clear cache for a specific model
self.env['res.country'].invalidate_model()
Real-World Examples in Odoo Core
The most effective way to see the intended usage of ormcache is through the implementation within Odoo. Two examples you will come across regularly are as follows.
IR Config Parameters
This is arguably the most significant application of ormcache throughout the whole program. Every time get_param() is called (used repeatedly throughout Odoo), it is stored in cache based on the key:
# In ir.config_parameter
@tools.ormcache('key')
def get_param(self, key, default=False):
self.env.cr.execute(
"SELECT value FROM ir_config_parameter WHERE key = %s", [key])
result = self.env.cr.fetchone()
return result[0] if result else default
That's why reading system parameters happens so quickly in production — once the first request has been made, everything comes from memory afterward.
Company Currency
# In res.company
@tools.ormcache('self.env.uid')
def _get_user_currency(self):
return self.env.user.company_id.currency_id
Field-Level Caching with store=True
Apart from ormcache, perhaps the simplest way to avoid excessive querying is to cache fields that have already been calculated inside the database rather than recalculating them for every request.
total_amount = fields.Float(
string='Total Amount',
compute='_compute_total_amount',
store=True # Written to DB, not recalculated on every read
)
@api.depends('order_line.price_subtotal')
def _compute_total_amount(self):
for record in self:
record.total_amount = sum(
record.order_line.mapped('price_subtotal'))
Caching Best Practices
Do's
- Cache static or rarely-changing data — currencies, countries, config params
- Define clear invalidation triggers for every cache you add
- Use ormcache_context when results vary by language or company
- Store computed fields (store=True) when they're read far more than written
- Profile before and after to confirm the cache is actually helping
Don'ts
- Don't cache user-specific transactional data
- Don't cache anything that must be real-time, like live stock levels
- Don't cache large result sets — the memory overhead can outweigh the gains
- Don't forget to call clear_caches() in migration scripts after direct DB changes
Indeed, Odoo cache capabilities are quite potent, but @tools.ormcache is just the beginning. Once you know how it works and when you reach its limitations, you could combine different types of caching techniques in your application: from computed fields, cached through ormcache derivatives, up to Redis usage in multi-process situations or HTTP headers on public APIs.
In this respect, the most successful developers in optimizing Odoo applications would be the ones able to measure their own issues, find out what queries cause problems in their apps, use caching techniques appropriate to every case, and confirm their efficiency. In such a way, they will definitely achieve more than those trying to apply caches anywhere.
To read more about A Complete Introduction to Redis Caching in Odoo, refer to our blog A Complete Introduction to Redis Caching in Odoo.