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()

Reply via email to