[FIX] base: upsert translations during copy_translations

When doing a copy, create the new translations directly in SQL and handle
potential conflicts.

Conflicts can occure in case of reinstallation as showed in opw-1955062 and
opw-1950117.
In case of "leftovers" of translations (e.g. remaining after the uninstallation
of a module), creating the new translations (when reinstalling the module) may
produce a conflict with (type, name, res_id, lang) and raise an error.

This is NOT a problem of reading .po file during installation (which handles
correctly conflicts) but of business code creating new records and linked
translations (e.g. website copying website.menu records).

Removing old translations during uninstall is handled in a previous commit in
ir.model.fields _drop_column method.
This commit fixes the reinstallation on instances with leftover translations
and fixes the issue without needing a manual intervention (i.e. delete the old
translations manually).

Closes odoo/odoo#32056

Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
This commit is contained in:
Martin Trigaux
2019-04-04 14:41:26 +00:00
committed by Raphael Collet
parent 112bd835d8
commit 489494e733
2 changed files with 44 additions and 2 deletions
+41
View File
@@ -670,6 +670,47 @@ class IrTranslation(models.Model):
})
self._modified_model(field.model_name)
@api.model
def _upsert_translations(self, vals_list):
""" Insert or update translations of type 'model' or 'model_terms'.
This method is used for creations of translations where the given
``vals_list`` is trusted to be the right values and potential
conflicts should be updated to the new given value.
"""
rows_by_type = defaultdict(list)
for vals in vals_list:
rows_by_type[vals['type']].append((
vals['name'], vals['lang'], vals['res_id'], vals['src'], vals['type'],
vals.get('module'), vals['value'], vals.get('state'), vals.get('comments'),
))
if rows_by_type['model']:
query = """
INSERT INTO ir_translation (name, lang, res_id, src, type,
module, value, state, comments)
VALUES {}
ON CONFLICT (type, lang, name, res_id) WHERE type='model'
DO UPDATE SET (name, lang, res_id, src, type, value, module, state, comments) =
(EXCLUDED.name, EXCLUDED.lang, EXCLUDED.res_id, EXCLUDED.src, EXCLUDED.type,
EXCLUDED.value, EXCLUDED.module, EXCLUDED.state, EXCLUDED.comments)
WHERE EXCLUDED.value IS NOT NULL AND EXCLUDED.value != '';
""".format(", ".join(["%s"] * len(rows_by_type['model'])))
self.env.cr.execute(query, rows_by_type['model'])
if rows_by_type['model_terms']:
query = """
INSERT INTO ir_translation (name, lang, res_id, src, type,
module, value, state, comments)
VALUES {}
ON CONFLICT (type, name, lang, res_id, md5(src))
DO UPDATE SET (name, lang, res_id, src, type, value, module, state, comments) =
(EXCLUDED.name, EXCLUDED.lang, EXCLUDED.res_id, EXCLUDED.src, EXCLUDED.type,
EXCLUDED.value, EXCLUDED.module, EXCLUDED.state, EXCLUDED.comments)
WHERE EXCLUDED.value IS NOT NULL AND EXCLUDED.value != '';
""".format(", ".join(["%s"] * len(rows_by_type['model_terms'])))
self.env.cr.execute(query, rows_by_type['model_terms'])
@api.model
def translate_fields(self, model, id, field=None):
""" Open a view for translating the field(s) of the record (model, id). """
+3 -2
View File
@@ -4242,11 +4242,12 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
if vals['lang'] == old.env.lang and field.translate is True:
# force a source if the new_val was not changed by copy override
if new_val == old[name]:
vals['source'] = old_wo_lang[name]
new_wo_lang[name] = old_wo_lang[name]
vals['src'] = old_wo_lang[name]
# the value should be the new value (given by copy())
vals['value'] = new_val
vals_list.append(vals)
Translation.create(vals_list)
Translation._upsert_translations(vals_list)
@api.multi
@api.returns('self', lambda value: value.id)