[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.
This commit is contained in:
Raphael Collet
2016-01-28 16:33:09 +01:00
parent afd2dbfb7f
commit 995b257a6c
2 changed files with 38 additions and 12 deletions
@@ -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']
+11 -3
View File
@@ -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]