[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
This commit is contained in:
Rémy Voet (ryv)
2023-06-28 17:41:19 +02:00
parent 3c62ca1eb9
commit daf7ca521d
3 changed files with 22 additions and 8 deletions
+2 -7
View File
@@ -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)
@@ -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):
+9 -1
View File
@@ -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):