[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:
Adrian Torres
2020-07-16 11:09:05 +00:00
committed by Raphael Collet
parent 65b8751e32
commit 9fefa08745
2 changed files with 24 additions and 2 deletions
@@ -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
View File
@@ -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)