From a583a56324c615379e80952ae31a59efcd28d205 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 13 Feb 2018 17:31:40 +0100 Subject: [PATCH] [FIX] base, web: don't fold M2M when not in import compatible mode Before this commit, if the first exported field of an M2M is `id` (the xid) the entire M2M is folded into a single cell with comma-separated xids and any following field is ignored. If `id` is any but the first field, the export behaves normally (with the m2m exported as a "table" inside the parent record). This behaviour makes sense for import-compatible exports where the id is the only thing which can be exported anyway, but it is troublesome outside of that mode as the behaviour of m2m under export becomes incoherent/unpredictable (ish) as it depends on the position of the m2m's `id` in the exports list. Change it so we only perform folding in import-compatible mode (which is the default for backwards compatibility with e.g. API calling export_data directly & the like). opw-813361 Fixes #22600 --- addons/web/controllers/main.py | 2 +- odoo/addons/test_impex/tests/test_export.py | 49 ++++++++++++++++++++- odoo/models.py | 6 ++- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index ab96ee293d4..2a63740d424 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1335,7 +1335,7 @@ class ExportFormat(object): model, fields, ids, domain, import_compat = \ operator.itemgetter('model', 'fields', 'ids', 'domain', 'import_compat')(params) - Model = request.env[model].with_context(**params.get('context', {})) + Model = request.env[model].with_context(import_compat=import_compat, **params.get('context', {})) records = Model.browse(ids) or Model.search(domain, offset=0, limit=False, order=False) if not Model._is_an_ordinary_table(): diff --git a/odoo/addons/test_impex/tests/test_export.py b/odoo/addons/test_impex/tests/test_export.py index 74f62bf7189..974d20e223f 100644 --- a/odoo/addons/test_impex/tests/test_export.py +++ b/odoo/addons/test_impex/tests/test_export.py @@ -600,7 +600,54 @@ class test_m2m(CreatorCase): ['', u'export.many2many.other:13'], ]) - # essentially same as o2m, so boring + def test_multiple_records_subfield(self): + r = self.make(self.commands) + xid = self.env['ir.model.data'].create({ + 'name': 'whopwhopwhop', + 'module': '__t__', + 'model': r._name, + 'res_id': r.id, + }).complete_name + sids = [ + self.env['ir.model.data'].create({ + 'name': sub.str, + 'module': '__t__', + 'model': sub._name, + 'res_id': sub.id, + }).complete_name + for sub in r.value + ] + r.invalidate_cache() + + self.assertEqual( + r._export_rows([['value', 'id']]), + [['__t__.record000,__t__.record001,__t__.record010,__t__.record011,__t__.record100']] + ) + self.assertEqual( + r.with_context(import_compat=True)._export_rows([['value', 'id']]), + [['__t__.record000,__t__.record001,__t__.record010,__t__.record011,__t__.record100']] + ) + + self.assertEqual( + r.with_context(import_compat=False)._export_rows([['id'], ['value', 'id'], ['value', 'value']]), + [ + [xid, u'__t__.record000', u'4'], + [u'', u'__t__.record001', u'42'], + [u'', u'__t__.record010', u'36'], + [u'', u'__t__.record011', u'4'], + [u'', u'__t__.record100', u'13'] + ] + ) + self.assertEqual( + r.with_context(import_compat=False)._export_rows([['id'], ['value', 'value'], ['value', 'id']]), + [ + [xid, u'4', u'__t__.record000'], + [u'', u'42', u'__t__.record001'], + [u'', u'36', u'__t__.record010'], + [u'', u'4', u'__t__.record011'], + [u'', u'13', u'__t__.record100'] + ] + ) class test_function(CreatorCase): diff --git a/odoo/models.py b/odoo/models.py index 611b2bdbff3..03f676812d2 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -758,6 +758,7 @@ class BaseModel(object): :param fields: list of lists of fields to traverse :return: list of lists of corresponding values """ + import_compatible = self.env.context.get('import_compat', True) lines = [] for record in self: # main line of record, initially empty @@ -791,8 +792,9 @@ class BaseModel(object): else: primary_done.append(name) - # This is a special case, its strange behavior is intended! - if field.type == 'many2many' and len(path) > 1 and path[1] == 'id': + # in import_compat mode, m2m should always be exported as + # a comma-separated list of xids in a single cell + if import_compatible and field.type == 'many2many' and len(path) > 1 and path[1] == 'id': xml_ids = [r.__export_xml_id() for r in value] current[i] = ','.join(xml_ids) or False continue