[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 <rco@odoo.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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']
|
||||
|
||||
+17
-8
@@ -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)
|
||||
|
||||
+1
-1
@@ -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\''
|
||||
|
||||
Reference in New Issue
Block a user