Call getter function when accessing Function field

If the record is unsaved and the getter is based on on_change_with, we can call
the getter to compute the value.
This value must not be stored in the cache as it may change prior to saving.

issue4193
issue9263
review317291002
This commit is contained in:
Cédric Krier
2020-05-03 14:34:44 +02:00
parent ec4a8e9a90
commit fc66c17eb0
5 changed files with 80 additions and 0 deletions
+1
View File
@@ -1,3 +1,4 @@
* Call getter function when accessing Function field on unsaved record
* Add language configuration wizard
* Allow copying attachments and notes to created records
* Add link button on form
+13
View File
@@ -125,6 +125,19 @@ class Function(Field):
'ir.msg_setter_function_missing',
**Model.__names__(self.name)))
def __get__(self, inst, cls):
try:
return super().__get__(inst, cls)
except AttributeError:
if not self.getter.startswith('on_change_with'):
raise
value = getattr(inst, self.getter)(self.name)
# Use temporary instance to not modify instance values
temp_inst = cls()
# Set the value to have proper type
self.__set__(temp_inst, value)
return super().__get__(temp_inst, cls)
def __set__(self, inst, value):
self._field.__set__(inst, value)
+2
View File
@@ -19,6 +19,7 @@ def register():
from . import field_datetime
from . import field_dict
from . import field_float
from . import field_function
from . import field_integer
from . import field_many2many
from . import field_many2one
@@ -58,6 +59,7 @@ def register():
field_datetime.register('tests')
field_dict.register('tests')
field_float.register('tests')
field_function.register('tests')
field_integer.register('tests')
field_many2many.register('tests')
field_many2one.register('tests')
+32
View File
@@ -0,0 +1,32 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, fields
from trytond.pool import Pool
class FunctionAccessor(ModelSQL):
"Function Accessor"
__name__ = 'test.function.accessor'
target = fields.Many2One('test.function.accessor.target', "Target")
function = fields.Function(
fields.Many2One('test.function.accessor.target', "Function"),
'on_change_with_function')
@fields.depends('target')
def on_change_with_function(self, name=None):
if self.target:
return self.target.id
class FunctionAccessorTarget(ModelSQL):
"Function Accessor Target"
__name__ = 'test.function.accessor.target'
def register(module):
Pool.register(
FunctionAccessor,
FunctionAccessorTarget,
module=module, type_='model')
+32
View File
@@ -0,0 +1,32 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import unittest
from trytond.pool import Pool
from trytond.tests.test_tryton import activate_module, with_transaction
class FieldFunctionTestCase(unittest.TestCase):
"Test Field Function"
@classmethod
def setUpClass(cls):
activate_module('tests')
@with_transaction()
def test_accessor(self):
"Test accessing field on unsaved instance"
pool = Pool()
Model = pool.get('test.function.accessor')
Target = pool.get('test.function.accessor.target')
target = Target()
target.save()
record = Model()
record.target = target
self.assertEqual(record.function, target)
def suite():
return unittest.TestLoader().loadTestsFromTestCase(FieldFunctionTestCase)