changeset 42f9ed36b8e9 in trytond:default
details: https://hg.tryton.org/trytond?cmd=changeset;node=42f9ed36b8e9
description:
Ensure context is correctly set when initializing Model
We make the assignation twice if any field has a context defined. This
ensures
that the context will be evaluated with all initialized values when
the target records will be instantiated.
issue8586
review282041002
diffstat:
trytond/model/model.py | 29 +++++++++----------
trytond/tests/model.py | 25 +++++++++++++++++
trytond/tests/test_model.py | 65 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 104 insertions(+), 15 deletions(-)
diffs (176 lines):
diff -r d592efdea382 -r 42f9ed36b8e9 trytond/model/model.py
--- a/trytond/model/model.py Sun Sep 08 22:11:31 2019 +0200
+++ b/trytond/model/model.py Mon Sep 09 22:41:54 2019 +0200
@@ -2,6 +2,7 @@
# this repository contains the full copyright notices and license terms.
import copy
+from collections import defaultdict
from functools import total_ordering
from trytond.i18n import lazy_gettext
@@ -247,27 +248,25 @@
self._id = id
if kwargs:
self._values = {}
- parent_values = {}
+ parent_values = defaultdict(dict)
+ has_context = {}
for name, value in kwargs.items():
if not name.startswith('_parent_'):
setattr(self, name, value)
else:
- parent_values[name] = value
-
- def set_parent_value(record, name, value):
- parent_name, field = name.split('.', 1)
- parent_name = parent_name[8:] # Strip '_parent_'
- parent = getattr(record, parent_name, None)
- if parent is not None:
- if not field.startswith('_parent_'):
- setattr(parent, field, value)
- else:
- set_parent_value(parent, field, value)
- else:
- setattr(record, parent_name, {field: value})
+ name, field = name.split('.', 1)
+ name = name[len('_parent_'):]
+ parent_values[name][field] = value
+ value = parent_values[name]
+ if getattr(self.__class__, name).context:
+ has_context[name] = value
for name, value in parent_values.items():
- set_parent_value(self, name, value)
+ setattr(self, name, value)
+ # Set field with context a second times
+ # to ensure it was evaluated with all the fields
+ for name, value in has_context.items():
+ setattr(self, name, value)
self._init_values = self._values.copy()
else:
self._values = None
diff -r d592efdea382 -r 42f9ed36b8e9 trytond/tests/model.py
--- a/trytond/tests/model.py Sun Sep 08 22:11:31 2019 +0200
+++ b/trytond/tests/model.py Mon Sep 09 22:41:54 2019 +0200
@@ -3,6 +3,7 @@
from trytond.model import (ModelSingleton, ModelSQL, UnionMixin, fields,
sequence_ordered)
from trytond.pool import Pool
+from trytond.pyson import Eval
class Model(ModelSQL):
@@ -31,6 +32,28 @@
parent = fields.Many2One('test.model_child', "Parent")
+class ModelContext(Model):
+ "Model with contextual field"
+ __name__ = 'test.model_context'
+ name = fields.Char("Name")
+ target = fields.Many2One(
+ 'test.model', "Target",
+ context={
+ 'name': Eval('name'),
+ })
+
+
+class ModelContextParent(Model):
+ "Model with contextual field from _parent"
+ __name__ = 'test.model_context_parent'
+ parent = fields.Many2One('test.model', "Parent")
+ target = fields.Many2One(
+ 'test.model', "Target",
+ context={
+ 'name': Eval('_parent_parent', {}).get('name'),
+ })
+
+
class Singleton(ModelSingleton, ModelSQL):
'Singleton'
__name__ = 'test.singleton'
@@ -129,6 +152,8 @@
ModelParent,
ModelChild,
ModelChildChild,
+ ModelContext,
+ ModelContextParent,
Singleton,
URLObject,
Model4Union1,
diff -r d592efdea382 -r 42f9ed36b8e9 trytond/tests/test_model.py
--- a/trytond/tests/test_model.py Sun Sep 08 22:11:31 2019 +0200
+++ b/trytond/tests/test_model.py Mon Sep 09 22:41:54 2019 +0200
@@ -57,6 +57,71 @@
self.assertEqual(record.parent.parent.name, "Test 2")
@with_transaction()
+ def test_init_context(self):
+ "Test __init__ for field with context"
+ pool = Pool()
+ Model = pool.get('test.model')
+ ModelContext = pool.get('test.model_context')
+
+ target, = Model.create([{'name': "Test Context"}])
+ # The dictionary order is important
+ values = {
+ 'target': target.id,
+ 'name': "foo",
+ }
+ record = ModelContext(**values)
+
+ self.assertEqual(record.target._context, {'name': "foo"})
+
+ @with_transaction()
+ def test_init_parent_context(self):
+ "Test __init__ with _parent for field with context"
+ pool = Pool()
+ ModelContext = pool.get('test.model_context')
+
+ # The dictionary order is important
+ values = {
+ '_parent_target.name': "Test Context",
+ 'name': "foo",
+ }
+ record = ModelContext(**values)
+
+ self.assertEqual(record.target._context, {'name': "foo"})
+
+ @with_transaction()
+ def test_init_context_parent(self):
+ "Test __init__ for field with context from _parent"
+ pool = Pool()
+ Model = pool.get('test.model')
+ ModelContext = pool.get('test.model_context_parent')
+
+ target, = Model.create([{'name': "Test Context"}])
+ # The dictionary order is important
+ values = {
+ 'target': target.id,
+ '_parent_parent.name': "bar",
+ }
+ record = ModelContext(**values)
+
+ self.assertEqual(record.target._context, {'name': "bar"})
+
+ @with_transaction()
+ def test_init_parent_context_parent(self):
+ "Test __init__ with _parent for field with context from _parent"
+ pool = Pool()
+ ModelContext = pool.get('test.model_context_parent')
+
+ # The dictionary order is important
+ values = {
+ '_parent_target.name': "Test Context",
+ '_parent_parent.name': "bar",
+ }
+ record = ModelContext(**values)
+
+ self.assertEqual(record.parent.name, "bar")
+ self.assertEqual(record.target._context, {'name': "bar"})
+
+ @with_transaction()
def test_names_model(self):
"Test __names__ for model only"
pool = Pool()