[FIX] core: fix cached translations
before this commit: translations updated by `update_field_translation` api cannot be detected by t-cache and some cached data whose model overrides `write` with an extra 'clear_caches()' Step to reproduce: - Create a mega menu, select any template, `Odoo Menu` for the example - Install another language on the website - Go to the translated version of your website and enter translate mode - Change "Camera" in the mega menu to something else - Save The change won't be replicated, looking like it did nothing. From there, removing or adding `edit_translations=1` in the URL will use different cache version of the page's views and you will see the outdated value on one and the correct on the other one. after this commit: `update_field_translation` will call `write` it does the following 4 important things 1. mark field as modified 2. execute logics in the override `write` method 3. update write_date if needed to support t-cache opw-3305117 X-original-commit: 2beb466668e4eb80d7c3ca3947445fb1cb141cff Part-of: odoo/odoo#135277
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
import json
|
||||
|
||||
from hashlib import sha256
|
||||
from lxml import html
|
||||
from unittest.mock import Mock, patch
|
||||
from werkzeug.urls import url_parse
|
||||
|
||||
@@ -301,3 +303,35 @@ class TestMenuHttp(common.HttpCase):
|
||||
self.assertFalse(self.menu.page_id, "M2o should have been unset as this is an anchor URL.")
|
||||
self.assertEqual(self.menu.url, self.page_url + '#anchor', "Page URL should have been properly prefixed with the referer url")
|
||||
self.assertEqual(self.page.url, self.page_url, "Page URL should not have changed")
|
||||
|
||||
def test_03_mega_menu_translate(self):
|
||||
# Setup
|
||||
fr = self.env['res.lang']._activate_lang('fr_FR')
|
||||
Menu = self.env['website.menu']
|
||||
website = self.env['website'].browse(1)
|
||||
website.language_ids += fr
|
||||
menu = Menu.create({
|
||||
'name': 'Test Mega Menu Content Translation Edit Mode',
|
||||
'mega_menu_content': '<p>something</p>',
|
||||
'parent_id': website.menu_id.id,
|
||||
'website_id': website.id,
|
||||
})
|
||||
self.env['ir.module.module']._load_module_terms(['website'], [fr.code])
|
||||
|
||||
# Load cache
|
||||
self.url_open('/%s' % fr.url_code)
|
||||
self.url_open('/%s?edit_translations=1' % fr.url_code)
|
||||
|
||||
# Translate
|
||||
root = html.fromstring(menu.mega_menu_content)
|
||||
to_translate = root.text_content()
|
||||
sha = sha256(to_translate.encode()).hexdigest()
|
||||
menu.update_field_translations_sha('mega_menu_content', {fr.code: {sha: 'french_mega_menu_content'}})
|
||||
self.assertIn("french_mega_menu_content",
|
||||
menu.with_context(lang=fr.code, website_id=website.id).mega_menu_content)
|
||||
|
||||
# Checks
|
||||
page = self.url_open('/%s' % fr.url_code)
|
||||
self.assertIn(b"french_mega_menu_content", page.content)
|
||||
page = self.url_open('/%s?edit_translations=1' % fr.url_code)
|
||||
self.assertIn(b"french_mega_menu_content", page.content)
|
||||
|
||||
@@ -986,8 +986,9 @@ class IrModelFields(models.Model):
|
||||
# names of the models to patch
|
||||
patched_models = set()
|
||||
if vals and self:
|
||||
translate_only = all(self._fields[field_name].translate for field_name in vals)
|
||||
for item in self:
|
||||
if item.state != 'manual':
|
||||
if item.state != 'manual' and not translate_only:
|
||||
raise UserError(_('Properties of base fields cannot be altered in this manner! '
|
||||
'Please modify them through Python code, '
|
||||
'preferably through a custom addon!'))
|
||||
@@ -1056,12 +1057,6 @@ class IrModelFields(models.Model):
|
||||
|
||||
return res
|
||||
|
||||
def update_field_translations(self, field_name, translations):
|
||||
res = super().update_field_translations(field_name, translations)
|
||||
if res:
|
||||
self.env.registry.clear_cache()
|
||||
return res
|
||||
|
||||
@api.depends('field_description', 'model')
|
||||
def _compute_display_name(self):
|
||||
for field in self:
|
||||
|
||||
@@ -540,10 +540,7 @@ actual arch.
|
||||
return super(View, self).unlink()
|
||||
|
||||
def _update_field_translations(self, fname, translations, digest=None):
|
||||
res = super()._update_field_translations(fname, translations, digest)
|
||||
if fname == 'arch_db' and 'install_filename' not in self._context:
|
||||
self.write({'arch_updated': True})
|
||||
return res
|
||||
return super(View, self.with_context(no_save_prev=True))._update_field_translations(fname, translations, digest)
|
||||
|
||||
@api.returns('self', lambda value: value.id)
|
||||
def copy(self, default=None):
|
||||
|
||||
@@ -1749,6 +1749,20 @@ class RelatedTranslation2(models.Model):
|
||||
parent_id = fields.Many2one('test_new_api.related_translation_1', string='Parent Model')
|
||||
name = fields.Char('Name Related', related='parent_id.name', readonly=False)
|
||||
html = fields.Html('HTML Related', related='parent_id.html', readonly=False)
|
||||
computed_name = fields.Char('Name Computed', compute='_compute_name')
|
||||
computed_html = fields.Char('HTML Computed', compute='_compute_html')
|
||||
|
||||
@api.depends_context('lang')
|
||||
@api.depends('parent_id.name')
|
||||
def _compute_name(self):
|
||||
for record in self:
|
||||
record.computed_name = record.parent_id.name
|
||||
|
||||
@api.depends_context('lang')
|
||||
@api.depends('parent_id.html')
|
||||
def _compute_html(self):
|
||||
for record in self:
|
||||
record.computed_html = record.parent_id.html
|
||||
|
||||
|
||||
class RelatedTranslation3(models.Model):
|
||||
|
||||
@@ -89,6 +89,8 @@ class TestRelatedTranslation(odoo.tests.TransactionCase):
|
||||
self.assertEqual(self.test1.with_context(lang='fr_FR').name, 'Couteau')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').name, 'New knife')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').name, 'Couteau')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').computed_name, 'New knife')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').computed_name, 'Couteau')
|
||||
self.assertEqual(self.test3.with_context(lang='en_US').name, 'New knife')
|
||||
self.assertEqual(self.test3.with_context(lang='fr_FR').name, 'Couteau')
|
||||
self.test1.update_field_translations('name', {'fr_FR': 'Nouveau couteau'})
|
||||
@@ -96,6 +98,8 @@ class TestRelatedTranslation(odoo.tests.TransactionCase):
|
||||
self.assertEqual(self.test1.with_context(lang='fr_FR').name, 'Nouveau couteau')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').name, 'New knife')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').name, 'Nouveau couteau')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').computed_name, 'New knife')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').computed_name, 'Nouveau couteau')
|
||||
self.assertEqual(self.test3.with_context(lang='en_US').name, 'New knife')
|
||||
self.assertEqual(self.test3.with_context(lang='fr_FR').name, 'Nouveau couteau')
|
||||
|
||||
@@ -105,6 +109,8 @@ class TestRelatedTranslation(odoo.tests.TransactionCase):
|
||||
self.assertEqual(self.test1.with_context(lang='fr_FR').name, 'Couteau')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').name, 'New knife')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').name, 'Couteau')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').computed_name, 'New knife')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').computed_name, 'Couteau')
|
||||
self.assertEqual(self.test3.with_context(lang='en_US').name, 'New knife')
|
||||
self.assertEqual(self.test3.with_context(lang='fr_FR').name, 'Couteau')
|
||||
self.test3.update_field_translations('name', {'fr_FR': 'Nouveau couteau'})
|
||||
@@ -112,6 +118,8 @@ class TestRelatedTranslation(odoo.tests.TransactionCase):
|
||||
self.assertEqual(self.test1.with_context(lang='fr_FR').name, 'Nouveau couteau')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').name, 'New knife')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').name, 'Nouveau couteau')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').computed_name, 'New knife')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').computed_name, 'Nouveau couteau')
|
||||
self.assertEqual(self.test3.with_context(lang='en_US').name, 'New knife')
|
||||
self.assertEqual(self.test3.with_context(lang='fr_FR').name, 'Nouveau couteau')
|
||||
|
||||
@@ -136,6 +144,8 @@ class TestRelatedTranslation(odoo.tests.TransactionCase):
|
||||
self.assertEqual(self.test1.with_context(lang='fr_FR').html, '<p>Couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').html, '<p>Couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').computed_html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').computed_html, '<p>Couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test3.with_context(lang='en_US').html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test3.with_context(lang='fr_FR').html, '<p>Couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.test1.update_field_translations('html', {'fr_FR': {'Couteau': 'Nouveau couteau'}})
|
||||
@@ -143,6 +153,8 @@ class TestRelatedTranslation(odoo.tests.TransactionCase):
|
||||
self.assertEqual(self.test1.with_context(lang='fr_FR').html, '<p>Nouveau couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').html, '<p>Nouveau couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').computed_html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').computed_html, '<p>Nouveau couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test3.with_context(lang='en_US').html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test3.with_context(lang='fr_FR').html, '<p>Nouveau couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
|
||||
@@ -151,6 +163,8 @@ class TestRelatedTranslation(odoo.tests.TransactionCase):
|
||||
self.assertEqual(self.test1.with_context(lang='fr_FR').html, '<p>Couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').html, '<p>Couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').computed_html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').computed_html, '<p>Couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test3.with_context(lang='en_US').html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test3.with_context(lang='fr_FR').html, '<p>Couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.test3.update_field_translations('html', {'fr_FR': {'Couteau': 'Nouveau couteau'}})
|
||||
@@ -158,6 +172,8 @@ class TestRelatedTranslation(odoo.tests.TransactionCase):
|
||||
self.assertEqual(self.test1.with_context(lang='fr_FR').html, '<p>Nouveau couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').html, '<p>Nouveau couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='en_US').computed_html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test2.with_context(lang='fr_FR').computed_html, '<p>Nouveau couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
self.assertEqual(self.test3.with_context(lang='en_US').html, '<p>Knife</p><p>Fork</p><p>Spoon</p>')
|
||||
self.assertEqual(self.test3.with_context(lang='fr_FR').html, '<p>Nouveau couteau</p><p>Fourchette</p><p>Cuiller</p>')
|
||||
|
||||
|
||||
+11
-5
@@ -3490,6 +3490,10 @@ class BaseModel(metaclass=MetaModel):
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
self.check_access_rights('write')
|
||||
self.check_field_access_rights('write', [field_name])
|
||||
self.check_access_rule('write')
|
||||
|
||||
valid_langs = set(code for code, _ in self.env['res.lang'].get_installed()) | {'en_US'}
|
||||
missing_langs = set(translations) - valid_langs
|
||||
if missing_langs:
|
||||
@@ -3516,9 +3520,6 @@ class BaseModel(metaclass=MetaModel):
|
||||
if field.related and not field.store:
|
||||
related_path, field_name = field.related.rsplit(".", 1)
|
||||
return self.mapped(related_path)._update_field_translations(field_name, translations, digest)
|
||||
self.check_access_rights('write')
|
||||
self.check_field_access_rights('write', [field_name])
|
||||
self.check_access_rule('write')
|
||||
|
||||
if field.translate is True:
|
||||
# falsy values (except emtpy str) are used to void the corresponding translation
|
||||
@@ -3545,7 +3546,6 @@ class BaseModel(metaclass=MetaModel):
|
||||
'{{}}'::jsonb)
|
||||
WHERE id = %s
|
||||
''', (Json({'en_US': translation_fallback}), Json(translations), self.id))
|
||||
self.modified([field_name])
|
||||
else:
|
||||
# Note:
|
||||
# update terms in 'en_US' will not change its value other translated values
|
||||
@@ -3573,7 +3573,13 @@ class BaseModel(metaclass=MetaModel):
|
||||
}
|
||||
new_translations[lang] = field.translate(translation.get, old_value)
|
||||
self.env.cache.update_raw(self, field, [new_translations], dirty=True)
|
||||
self.modified([field_name])
|
||||
|
||||
# the following write is incharge of
|
||||
# 1. mark field as modified
|
||||
# 2. execute logics in the override `write` method
|
||||
# 3. update write_date of the record if exists to support 't-cache'
|
||||
# even if the value in cache is the same as the value written
|
||||
self[field_name] = self[field_name]
|
||||
return True
|
||||
|
||||
def get_field_translations(self, field_name, langs=None):
|
||||
|
||||
@@ -1397,6 +1397,7 @@ class TranslationImporter:
|
||||
self.model_translations.clear()
|
||||
|
||||
env.invalidate_all()
|
||||
env.registry.clear_cache()
|
||||
if self.verbose:
|
||||
_logger.info("translations are loaded successfully")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user