From b92415023dfd73b33c2d4b203908f7aa6cb7aa4d Mon Sep 17 00:00:00 2001 From: "Chong Wang (cwg)" Date: Wed, 29 Mar 2023 12:01:44 +0000 Subject: [PATCH] [IMP] core: prefetch all translations a new context `prefetch_langs=True` allows ORM to prefetch all translations of translated fields while fetching. For example The activated languages are 'fr_FR' and 'nl_NL' In the database the value is '{"en_US": "English", "fr_FR": "French"}'::jsonb after fetch with `prefetch_langs=True` the raw cache value will become {'en_US': 'English', 'fr_FR': 'French', 'nl_NL': 'English'} closes odoo/odoo#116947 Signed-off-by: Raphael Collet --- addons/website/models/website.py | 5 +++- odoo/addons/base/tests/test_translate.py | 31 ++++++++++++++++++++++++ odoo/api.py | 25 +++++++++++++------ odoo/models.py | 2 +- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index c298f0835b7..06df281684f 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -886,6 +886,7 @@ class Website(models.Model): langs = [] shorts = [] + self_prefetch_langs = self.with_context(prefetch_langs=True) for lg in languages: lg_codes = lg.code.split('_') short = lg_codes[0] @@ -893,7 +894,7 @@ class Website(models.Model): langs.append({ 'hreflang': ('-'.join(lg_codes)).lower(), 'short': short, - 'href': self._get_canonical_url_localized(lang=lg, canonical_params=canonical_params), + 'href': self_prefetch_langs._get_canonical_url_localized(lang=lg, canonical_params=canonical_params), }) # if there is only one region for a language, use only the language code @@ -1292,6 +1293,8 @@ class Website(models.Model): args[key] = val = val.with_user(request.uid) if val.env.context.get('lang') != lang.code: args[key] = val = val.with_context(lang=lang.code) + if self.env.context.get('prefetch_langs'): + args[key] = val = val.with_context(prefetch_langs=True) router = http.root.get_db_router(request.db).bind('') path = router.build(rule.endpoint, args) diff --git a/odoo/addons/base/tests/test_translate.py b/odoo/addons/base/tests/test_translate.py index f83ac3ff252..373e95b3aa4 100644 --- a/odoo/addons/base/tests/test_translate.py +++ b/odoo/addons/base/tests/test_translate.py @@ -482,6 +482,37 @@ class TestTranslation(TransactionCase): category_in = CategoryEs.search([('name', 'in', ['Customers'])]) self.assertIn(self.customers, category_in, "Search with 'in' should use the English name if the current language translation is not available") + def test_111_prefetch_langs(self): + category_en = self.customers.with_context(lang='en_US') + + self.env.ref('base.lang_nl').active = True + category_nl = category_en.with_context(lang='nl_NL') + category_nl.name = 'Klanten' + + self.assertTrue(self.env.ref('base.lang_fr').active) + category_fr = category_en.with_context(lang='fr_FR') + + self.assertFalse(self.env.ref('base.lang_zh_CN').active) + category_zh = category_en.with_context(lang='zh_CN') + + self.env['res.partner'].with_context(active_test=False).search([]).write({'lang': 'fr_FR'}) + self.env.ref('base.lang_en').active = False + + category_fr.with_context(prefetch_langs=True).name + category_nl.name + category_en.name + category_zh.name + category_fr.invalidate_recordset() + + with self.assertQueryCount(1): + self.assertEqual(category_fr.with_context(prefetch_langs=True).name, 'Clients') + + with self.assertQueryCount(0): + self.assertEqual(category_nl.name, 'Klanten') + self.assertEqual(category_en.name, 'Customers') + self.assertEqual(category_zh.name, 'Customers') + + # TODO Currently, the unique constraint doesn't work for translatable field # def test_111_unique_en(self): # Country = self.env['res.country'] diff --git a/odoo/api.py b/odoo/api.py index eff497c43be..56c48c414de 100644 --- a/odoo/api.py +++ b/odoo/api.py @@ -1055,14 +1055,23 @@ class Cache(object): """ field_cache = self._set_field_cache(records, field) if field.translate: - lang = records.env.lang or 'en_US' - for id_, val in zip(records._ids, values): - if val is None: - field_cache.setdefault(id_, None) - else: - cache_value = field_cache.setdefault(id_, {}) - if cache_value is not None: - cache_value.setdefault(lang, val) + if records.env.context.get('prefetch_langs'): + langs = {lang for lang, _ in records.env['res.lang'].get_installed()} | {'en_US'} + for id_, val in zip(records._ids, values): + if val is None: + field_cache.setdefault(id_, None) + else: + val_all_en = dict.fromkeys(langs, val['en_US']) + field_cache[id_] = {**val_all_en, **val} + else: + lang = records.env.lang or 'en_US' + for id_, val in zip(records._ids, values): + if val is None: + field_cache.setdefault(id_, None) + else: + cache_value = field_cache.setdefault(id_, {}) + if cache_value is not None: + cache_value.setdefault(lang, val) else: for id_, val in zip(records._ids, values): field_cache.setdefault(id_, val) diff --git a/odoo/models.py b/odoo/models.py index 8150c6b678f..67f43e04c9a 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -2641,7 +2641,7 @@ class BaseModel(metaclass=MetaModel): extra=extra, extra_params=extra_params, ) return '"%s"."%s"' % (rel_alias, field.column2) - elif field.translate: + elif field.translate and not self.env.context.get('prefetch_langs'): lang = self.env.lang or 'en_US' if lang == 'en_US': return f'"{alias}"."{fname}"->>\'en_US\''