[FIX] core: mapped() does not prefetch as expected
Before this commit, calling `mapped()` on a relational field of a single
record inside a for loop does not correctly propagate the prefetch ids
from the bigger recordset down to the record inside the loop:
[rec.line_ids.mapped('name') for rec in recs]
To be more precise, `recs` correctly propagates prefetching information
down to `rec.line_ids`, but method `mapped()` does not pass it along to
the record that triggers the prefetching.
This resulted in one query per record in `recs`, so if `recs` were a
1000 records recordset, at least 1000 queries would be necessary.
With this commit, the `mapped()` function correctly propagates the
`_prefetch_ids` of the larger recordset (`rec.line_ids`) so that the
prefetching works properly and the 1000 queries are brought down to 1.
This commit is a followup on #42611.
closes odoo/odoo#54565
X-original-commit: 0e97053fee36c0c76b70cb94be5340c3144b178b
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
Signed-off-by: Adrian Torres (adt) <adt@odoo.com>
This commit is contained in:
committed by
Raphael Collet
parent
65b8751e32
commit
9fefa08745
@@ -535,3 +535,21 @@ class TestIrPropertyOptimizations(TransactionCase):
|
||||
|
||||
with self.assertQueryCount(5):
|
||||
self.Bacon.create({'property_eggs': False})
|
||||
|
||||
|
||||
@tagged('mapped_perf')
|
||||
class TestMapped(TransactionCase):
|
||||
|
||||
def test_relational_mapped(self):
|
||||
# create 1000 records with one line each
|
||||
recs = self.env['test_performance.base'].create([
|
||||
{'name': 'foo%d' % index, 'line_ids': [(0, 0, {'value': index})]}
|
||||
for index in range(1000)
|
||||
])
|
||||
recs.flush()
|
||||
recs.invalidate_cache()
|
||||
|
||||
# expected same performance as recs.line_ids.mapped('value')
|
||||
with self.assertQueryCount(3):
|
||||
for rec in recs:
|
||||
rec.line_ids.mapped('value')
|
||||
|
||||
+6
-2
@@ -1018,8 +1018,12 @@ class Field(MetaField('DummyField', (object,), {})):
|
||||
# retrieve values in cache, and fetch missing ones
|
||||
vals = records.env.cache.get_until_miss(records, self)
|
||||
while len(vals) < len(records):
|
||||
# trigger prefetching on remaining records, and continue retrieval
|
||||
remaining = records[len(vals):]
|
||||
# It is important to construct a 'remaining' recordset with the
|
||||
# _prefetch_ids of the original recordset, in order to prefetch as
|
||||
# many records as possible. If not done this way, scenarios such as
|
||||
# [rec.line_ids.mapped('name') for rec in recs] would generate one
|
||||
# query per record in `recs`!
|
||||
remaining = records._browse(records.env, records[len(vals):]._ids, records._prefetch_ids)
|
||||
self.__get__(first(remaining), type(remaining))
|
||||
vals += records.env.cache.get_until_miss(remaining, self)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user