Prefetch Patterns
At the point when you access information from a record-set, it makes a queries in the
data set. In the event that you have a record-set with different records, getting
records on it can make a framework slow in light of the various SQL queries. In this
recipe, we will investigate how you can utilize the prefetching example to address this
issue. By following the prefetching design, you can decrease the quantity of inquiries
required, which will further develop execution and make your framework quicker.
Investigate the accompanying code; it is a typical process technique. In this strategy,
self is a recordset of numerous records. At the point when you emphasize
straightforwardly on the recordset, prefetching works perfectly:
# Correct prefetching
def compute_method(self):
for rec in self:
print(rec.name)
Be that as it may, at times, prefetching turns out to be more complicated, for example,
while getting information with the browse strategy. In the accompanying model, we browse
records individually in the for loop. This won't utilize prefetching effectively and it
will execute more queries than expected:
# Incorrect prefetching
def some_action(self):
record_ids = []
self.env.cr.execute("some query to fetch record id")
for rec in self.env.cr.fetchall():
record = self.env['sale.order'].browse(rec[0])
print(record.name)
Instead of passing individual IDs to the browse method we can retrieve the IDs to a
variable and pass at once. Then you can perform the operations on this recordset.
Along these lines, you won't lose the prefetching element and information will be
brought in a solitary SQL queries.
# Correct prefetching
def some_action(self):
record_ids = []
self.env.cr.execute("some query to fetch record id")
record_ids = [rec[0] for rec in self.env.cr.fetchall()]
recordset = self.env['sale.order'].browse(record_ids)
for record in recordset:
print(record.name)
At the point when you are working with different recordsets, prefetching lessens the
quantity of SQL queries. It does this by getting each of the information without a
moment's delay. Ordinarily, prefetching works consequently in Odoo, yet you lose this
component in specific conditions, for example, when you split records as portrayed in
the accompanying model:
records = [rec for rec in record_ids if rec.id not in [111, 222, 444, 555]]
The above code given will part the recordset into parts, thus you can't exploit
prefetching.
Utilizing prefetching accurately can altogether work on the presentation of the
Object-Relational Mapping (ORM). We should investigate how prefetching functions in the
engine. At the point when you emphasize on a recordset through a for loop and access the
worth of a field in the first iteration, Rather than bringing information for the
ongoing record in the iteration, prefetching will get the information for the records as
a whole. The rationale behind this is that on the off chance that you are getting to a
field in a for circle, you are probably going to bring that information for the
following record in the iterations too. In the main emphasis of the for loop,
prefetching will get the information for all of the recordsets and keep it in the cache.
In the following iteration of the for loop, information will be served from this cache,
rather than making another SQL query. This will decrease the inquiry count from O(n) to
O(1).
We should assume the recordset has 10 records. At the point when you are in the first
loop and access the name field of the record, it will get the information for every one
of the 10 records. This isn't just the situation for the name field; it will likewise
bring every one of the fields for those 10 records. In the ensuing following iteration,
the information will be served from the store. This will decrease the quantity of
questions from 10 to 1:
self.env.cr.execute("select id from sale_order limit 10")
record = self.env['sale.order'].browse(record_ids)
for rec in record:
print(rec.name) # Prefetch name of all 10 records in the first loop
print(rec.attention) # Prefetch attention of all 10 records in the first loop
Note that the prefetching will get the worth of the entirety of the fields (with the
exception of the *2many fields) regardless of whether those fields are not utilized in
that frame of mind of the for loop. This is on the grounds that the additional sections
just minorly affect execution contrasted with the additional questions for every
segment.
Once in a while, prefetched fields could decrease execution. In these cases, you can
handicap prefetching by passing False into the prefetch_fields setting, as follows:
recordset.with_context(prefetch_fields=False). The prefetch component utilizes the
environment cache to store and recover record values. This implies that once the records
are gotten from the information base, all ensuing calls for fields will be served from
the environment cache. You can get to the environment cache utilizing the env.cache
trait. To nullify the cache, you can utilize the invalidate_cache() strategy for the
environment.
On the off chance that you split recordsets, the ORM will create a new recordset with a
new prefetch setting. Performing procedure on such recordsets will just prefetch the
information for the separate records. Assuming you need to prefetch every one of the
records after prefetch, you can do this by passing the prefetch record IDs to the
with_prefetch() technique. In the accompanying model, we split the recordset into two
sections. Here, we passed a typical prefetch setting in both recordsets, so when you get
the information from one of them, the ORM will bring the information for the other and
put the information in the cache for sometime later:
self.env.cr.execute("select id from sale_order limit 10")
record_ids = [rec[0] for rec in self.env.cr.fetchall()]
recordset = self.env['sale.order'].browse(record_ids)
recordset1 = recordset[:5]
for rec in recordset1:
print(rec.name) # Prefetch name of all 5 records in the first loop
print(rec.attention) # Prefetch attention of all 5 records in the first loop
recordset2 = recordset[5:].with_prefetch(recordset._ids)
for rec in recordset1:
print(rec.name) # Prefetch name of all 10 records in the first loop
print(rec.attention) # Prefetch attention of all 10 records in the first loop
In the above code recordsset1 prefetch only 5 records in the recordset while recordset2
prefetch all the records in the recordset. The prefetch setting isn't restricted to
parting recordsets. You can likewise utilize the with_prefetch() strategy to have a
typical prefetch setting between various recordsets. This implies that when you bring
information from one record, it will get information for any remaining recordsets as
well.