From 995b257a6c09b9dfd89eedac3a84ff36948b095b Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 28 Jan 2016 15:30:00 +0100 Subject: [PATCH] [FIX] fields: make `related_sudo` work for draft records (2) Traversing new records in a different cache requires that all new records on the path are copied across caches. Make the copy across caches recursive when the first record on the path is a new record. --- .../test_new_api/tests/test_new_fields.py | 36 ++++++++++++++----- openerp/fields.py | 14 ++++++-- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/openerp/addons/test_new_api/tests/test_new_fields.py b/openerp/addons/test_new_api/tests/test_new_fields.py index 59d85b211bf..6ee9a4a664e 100644 --- a/openerp/addons/test_new_api/tests/test_new_fields.py +++ b/openerp/addons/test_new_api/tests/test_new_fields.py @@ -393,28 +393,46 @@ class TestNewFields(common.TransactionCase): @mute_logger('openerp.addons.base.ir.ir_model') def test_41_new_related(self): - """ test the behavior of related fields on new records. """ - discussion = self.env.ref('test_new_api.discussion_0') - access = self.env.ref('test_new_api.access_discussion') - + """ test the behavior of related fields starting on new records. """ # make discussions unreadable for demo user + access = self.env.ref('test_new_api.access_discussion') access.write({'perm_read': False}) # create an environment for demo user - demo_env = self.env(user=self.env.ref('base.user_demo')) - self.assertEqual(demo_env.user.login, "demo") + env = self.env(user=self.env.ref('base.user_demo')) + self.assertEqual(env.user.login, "demo") # create a new message as demo user - values = {'discussion': discussion.id} - message = demo_env['test_new_api.message'].new(values) + discussion = self.env.ref('test_new_api.discussion_0') + message = env['test_new_api.message'].new({'discussion': discussion}) self.assertEqual(message.discussion, discussion) # read the related field discussion_name - self.assertEqual(message.discussion.env, demo_env) + self.assertEqual(message.discussion.env, env) self.assertEqual(message.discussion_name, discussion.name) with self.assertRaises(AccessError): message.discussion.name + @mute_logger('openerp.addons.base.ir.ir_model') + def test_42_new_related(self): + """ test the behavior of related fields traversing new records. """ + # make discussions unreadable for demo user + access = self.env.ref('test_new_api.access_discussion') + access.write({'perm_read': False}) + + # create an environment for demo user + env = self.env(user=self.env.ref('base.user_demo')) + self.assertEqual(env.user.login, "demo") + + # create a new discussion and a new message as demo user + discussion = env['test_new_api.discussion'].new({'name': 'Stuff'}) + message = env['test_new_api.message'].new({'discussion': discussion}) + self.assertEqual(message.discussion, discussion) + + # read the related field discussion_name + self.assertNotEqual(message.sudo().env, message.env) + self.assertEqual(message.discussion_name, discussion.name) + def test_50_defaults(self): """ test default values. """ fields = ['discussion', 'body', 'author', 'size'] diff --git a/openerp/fields.py b/openerp/fields.py index 30e0b97f918..a6775336a1e 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -58,6 +58,15 @@ def _check_value(value): """ Return ``value``, or call its getter if ``value`` is a :class:`SpecialValue`. """ return value.get() if isinstance(value, SpecialValue) else value +def copy_cache(records, env): + """ Recursively copy the cache of ``records`` to the environment ``env``. """ + for record, target in zip(records, records.with_env(env)): + if not target._cache: + for name, value in record._cache.iteritems(): + target[name] = value + if isinstance(value, BaseModel): + copy_cache(value, env) + def resolve_all_mro(cls, name, reverse=False): """ Return the (successively overridden) values of attribute ``name`` in ``cls`` @@ -538,10 +547,9 @@ class Field(object): # when related_sudo, bypass access rights checks when reading values others = records.sudo() if self.related_sudo else records for record, other in zip(records, others): - if not record.id: + if not record.id and record.env != other.env: # draft records: copy record's cache to other's cache first - for name, value in record._cache.iteritems(): - other[name] = value + copy_cache(record, other.env) # traverse the intermediate fields; follow the first record at each step for name in self.related[:-1]: other = other[name][:1]