From daf7ca521da77a97b303d697abb80bb0889a7d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Voet=20=28ryv=29?= Date: Tue, 30 May 2023 08:42:45 +0000 Subject: [PATCH] [IMP] core: fetch `display_name` during `name_search` The `name_search` makes at least 2 SQL requests, one to find the records and one to read fields needed to compute the `display_name`. With the default `_compute_display_name`, it only needs to fetch the `_rec_name` field (if there is one), but the ORM perfetch field mechanism will also fetch every prefetchable field (see `_fetch_field`). Then, to avoid running two queries and fetching too many fields, we add the dependency fields (with `_determine_fields_to_fetch`) to the select clause of the `Query` returned by `_name_search`. Part-of: odoo/odoo#122085 --- odoo/addons/base/tests/test_expression.py | 9 ++------- .../addons/test_performance/tests/test_performance.py | 11 +++++++++++ odoo/models.py | 10 +++++++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/odoo/addons/base/tests/test_expression.py b/odoo/addons/base/tests/test_expression.py index 067674c0648..9682783a89f 100644 --- a/odoo/addons/base/tests/test_expression.py +++ b/odoo/addons/base/tests/test_expression.py @@ -1228,12 +1228,8 @@ class TestQueries(TransactionCase): # search on both 'name' and 'model' self.assertEqual(Model._rec_names_search, ['name', 'model']) - # warmup - Model.name_search('foo') - - Model.name_search('partner') with self.assertQueries([''' - SELECT "ir_model"."id" + SELECT "ir_model"."id", "ir_model"."name"->>'en_US' FROM "ir_model" WHERE ( ("ir_model"."name"->>'en_US' ILIKE %s) @@ -1244,9 +1240,8 @@ class TestQueries(TransactionCase): ''']): Model.name_search('foo') - Model.name_search('partner', operator='not ilike') with self.assertQueries([''' - SELECT "ir_model"."id" + SELECT "ir_model"."id", "ir_model"."name"->>'en_US' FROM "ir_model" WHERE ( ("ir_model"."name" is NULL OR "ir_model"."name"->>'en_US' not ilike %s) diff --git a/odoo/addons/test_performance/tests/test_performance.py b/odoo/addons/test_performance/tests/test_performance.py index f15ff2bb85b..4129cd2dfd6 100644 --- a/odoo/addons/test_performance/tests/test_performance.py +++ b/odoo/addons/test_performance/tests/test_performance.py @@ -216,6 +216,17 @@ class TestPerformance(SavepointCaseWithUserDemo): expected, ) + @warmup + def test_name_search(self): + """ Test that `name` is fetch in the same query than the search + to compute `display_name` without any additional query """ + Model = self.env['test_performance.base'] + record = Model.create({'name': 'blablu'}) + record.invalidate_recordset() + + with self.assertQueryCount(1): + Model.name_search('blablu') + @users('__system__', 'demo') @warmup def test_write_base(self): diff --git a/odoo/models.py b/odoo/models.py index 71b30d62e92..fd6a6d32d2b 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -1685,7 +1685,15 @@ class BaseModel(metaclass=MetaModel): :return: list of pairs ``(id, text_repr)`` for all matching records. """ ids = self._name_search(name, args, operator, limit=limit, order=self._order) - return [(record.id, record.display_name) for record in self.browse(ids).sudo()] + + if isinstance(ids, Query): + records = self._fetch_query(ids, self._determine_fields_to_fetch(['display_name'])) + else: + # Some override of `_name_search` return list of ids. + records = self.browse(ids) + records.fetch(['display_name']) + + return [(record.id, record.display_name) for record in records.sudo()] @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None):