From 5a23c4dd1e1d09a6f4485ff782819032e3a24105 Mon Sep 17 00:00:00 2001 From: std-odoo Date: Wed, 9 Aug 2023 13:30:29 +0200 Subject: [PATCH] [FIX] web: fix the unity read with relational properties Bug === Since 7d2baaa0c726a7b0dd4fe7226862f960c9c59b73 , we read the field with `web_read`. We don't load the display name when calling read, but instead we load them after. But, since this change, the display names of the relational properties are not loaded. To fix that, we always load the display name of the relational properties because it's a purely frontend field. Task-3460126 Part-of: odoo/odoo#131394 --- .../test_new_api/tests/test_properties.py | 33 ++++++------------- .../test_new_api/tests/test_unity_read.py | 33 +++++++++++++++++++ odoo/fields.py | 25 +++++++------- odoo/models.py | 2 +- 4 files changed, 55 insertions(+), 38 deletions(-) diff --git a/odoo/addons/test_new_api/tests/test_properties.py b/odoo/addons/test_new_api/tests/test_properties.py index 525af4240fd..5a2efd3f5a9 100644 --- a/odoo/addons/test_new_api/tests/test_properties.py +++ b/odoo/addons/test_new_api/tests/test_properties.py @@ -646,14 +646,6 @@ class PropertiesCase(TestPropertiesMixin): msg='Definition must be present when reading child') self.assertEqual(many2one_property['value'], (self.partner.id, self.partner.display_name)) - # disable the display_name - properties_values = (self.message_1 | self.message_3).read(['attributes'], load=None) - many2one_property = properties_values[0]['attributes'][1] - - self.assertEqual( - many2one_property['value'], self.partner.id, - msg='If display_name is disable, should only return the record id') - def test_properties_field_many2one_basic(self): """Test the basic (read, write...) of the many2one property.""" self.message_2.attributes = [ @@ -1065,6 +1057,9 @@ class PropertiesCase(TestPropertiesMixin): 'comodel': 'test_new_api.partner', }] + def name_get(records): + return list(zip(records._ids, records.mapped('display_name'))) + with self.assertQueryCount(4): self.message_1.attributes = [ { @@ -1075,18 +1070,18 @@ class PropertiesCase(TestPropertiesMixin): "value": list(zip(partners[:10]._ids, partners[:10].mapped('display_name'))), } ] - attributes = self.message_1.read(['attributes'], load=None)[0]['attributes'] - self.assertEqual(attributes[0]['value'], partners[:10].ids) + attributes = self.message_1.read(['attributes'])[0]['attributes'] + self.assertEqual(attributes[0]['value'], name_get(partners[:10])) partners[:5].unlink() - with self.assertQueryCount(4): - attributes = self.message_1.read(['attributes'], load=None)[0]['attributes'] - self.assertEqual(attributes[0]['value'], partners[5:10].ids) + with self.assertQueryCount(5): + attributes = self.message_1.read(['attributes'])[0]['attributes'] + self.assertEqual(attributes[0]['value'], name_get(partners[5:10])) partners[5].unlink() with self.assertQueryCount(5): properties = self.message_1.read(['attributes'])[0]['attributes'] - self.assertEqual(properties[0]['value'], list(zip(partners[6:10]._ids, partners[6:10].mapped('display_name')))) + self.assertEqual(properties[0]['value'], name_get(partners[6:10])) # need to wait next write to clean data in database # a single read won't clean the removed many2many @@ -1097,14 +1092,6 @@ class PropertiesCase(TestPropertiesMixin): sql_values = self._get_sql_properties(self.message_1) self.assertEqual(sql_values, {'moderator_partner_ids': partners[6:10].ids}) - # read and disable display_name - properties = self.message_1.read(['attributes'], load=None)[0]['attributes'] - self.assertEqual( - properties[0]['value'], - partners[6:10].ids, - msg='Should not return the partners name', - ) - # Check that duplicated ids are removed self.env.flush_all() moderator_partner_ids = partners[6:10].ids @@ -1122,7 +1109,7 @@ class PropertiesCase(TestPropertiesMixin): properties = self.message_1.read(['attributes'], load=None)[0]['attributes'] self.assertEqual( properties[0]['value'], - partners[6:10].ids, + name_get(partners[6:10]), msg='Should removed duplicated ids', ) diff --git a/odoo/addons/test_new_api/tests/test_unity_read.py b/odoo/addons/test_new_api/tests/test_unity_read.py index b9c97d495b0..b7fd4138083 100644 --- a/odoo/addons/test_new_api/tests/test_unity_read.py +++ b/odoo/addons/test_new_api/tests/test_unity_read.py @@ -755,3 +755,36 @@ class TestUnityRead(TransactionCase): 'm2o_reference_model': False, } ]) + + def test_properties(self): + """Check that the display name of the relational properties are always loaded.""" + discussion = self.env['test_new_api.discussion'].create({ + 'name': 'Test Discussion', + 'attributes_definition': [{ + 'name': 'discussion_color_code', + 'string': 'Color Code', + 'type': 'char', + 'default': 'blue', + }, { + 'name': 'moderator_partner_id', + 'string': 'Partner', + 'type': 'many2one', + 'comodel': 'test_new_api.partner', + }], + 'participants': [Command.link(self.env.user.id)], + }) + partner = self.env['test_new_api.partner'].create({'name': 'Test Partner Properties'}) + message = self.env['test_new_api.message'].create({ + 'name': 'Test Message', + 'discussion': discussion.id, + 'author': self.env.user.id, + 'attributes': { + 'discussion_color_code': 'Test', + 'moderator_partner_id': partner.id, + }, + }) + values = message.web_read({'attributes': False})[0]['attributes'] + self.assertEqual(len(values), 2) + self.assertEqual(values[0]['name'], 'discussion_color_code') + self.assertEqual(values[1]['name'], 'moderator_partner_id') + self.assertEqual(values[1]['value'], (partner.id, 'Test Partner Properties')) diff --git a/odoo/fields.py b/odoo/fields.py index 32a25080f2b..a311a99385a 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -11,6 +11,7 @@ from xmlrpc.client import MAXINT import ast import base64 import copy +import contextlib import binascii import enum import itertools @@ -3334,9 +3335,9 @@ class Properties(Field): # }] # def convert_to_read(self, value, record, use_display_name=True): - return self.convert_to_read_multi([value], record, use_display_name)[0] + return self.convert_to_read_multi([value], record)[0] - def convert_to_read_multi(self, values, records, use_display_name=True): + def convert_to_read_multi(self, values, records): if not records: return values assert len(values) == len(records) @@ -3351,15 +3352,14 @@ class Properties(Field): assert isinstance(value, dict), f"Wrong type {value!r}" result.append(self._dict_to_list(value, definition)) - res_ids_per_model = self._get_res_ids_per_model(records, result, use_display_name) + res_ids_per_model = self._get_res_ids_per_model(records, result) # value is in record format for value in result: self._parse_json_types(value, records.env, res_ids_per_model) - if use_display_name: - for value in result: - self._add_display_name(value, records.env) + for value in result: + self._add_display_name(value, records.env) return result @@ -3382,7 +3382,7 @@ class Properties(Field): record._cache[self.definition_record] = snapshot[self.definition_record].id or None return super().convert_to_onchange(value, record, names) - def _get_res_ids_per_model(self, records, values_list, use_display_name=True): + def _get_res_ids_per_model(self, records, values_list): """Read everything needed in batch for the given records. To retrieve relational properties names, or to check their existence, @@ -3419,13 +3419,10 @@ class Properties(Field): recs = records.env[model].browse(ids).exists() res_ids_per_model[model] = set(recs.ids) - if use_display_name: - for record in recs: - # read a field to pre-fetch the recordset - try: - record.display_name - except AccessError: - pass + for record in recs: + # read a field to pre-fetch the recordset + with contextlib.suppress(AccessError): + record.display_name return res_ids_per_model diff --git a/odoo/models.py b/odoo/models.py index 7f20801c79c..15304a99f0c 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -3369,7 +3369,7 @@ class BaseModel(metaclass=MetaModel): except MissingError: vals.clear() - results = field.convert_to_read_multi(values_list, self.browse(records), use_display_name) + results = field.convert_to_read_multi(values_list, self.browse(records)) for record_read_vals, convert_result in zip(data, results): record_read_vals[1][name] = convert_result continue