diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py index 1e29cc0c9d5..c8cb5d2afc2 100644 --- a/openerp/addons/base/ir/ir_model.py +++ b/openerp/addons/base/ir/ir_model.py @@ -245,6 +245,14 @@ class ir_model_fields(osv.osv): 'relation_table': fields.char("Relation Table", help="Used for custom many2many fields to define a custom relation table name"), 'column1': fields.char("Column 1", help="Column referring to the record in the model table"), 'column2': fields.char("Column 2", help="Column referring to the record in the comodel table"), + 'compute': fields.text("Compute", help="Code to compute the value of the field.\n" + "Iterate on the recordset 'self' and assign the field's value:\n\n" + " for record in self:\n" + " record['size'] = len(record.name)\n\n" + "Modules time, datetime, dateutil are available."), + 'depends': fields.char("Dependencies", help="Dependencies of compute method; " + "a list of comma-separated field names, like\n\n" + " name, partner_id.name"), } _rec_name='field_description' _defaults = { @@ -322,6 +330,12 @@ class ir_model_fields(osv.osv): self.readonly = True self.copy = False + @api.onchange('compute') + def _onchange_compute(self): + if self.compute: + self.readonly = True + self.copy = False + @api.one @api.constrains('relation_table') def _check_relation_table(self): diff --git a/openerp/addons/base/ir/ir_model_view.xml b/openerp/addons/base/ir/ir_model_view.xml index 9edc2af9d2c..715f966bc88 100644 --- a/openerp/addons/base/ir/ir_model_view.xml +++ b/openerp/addons/base/ir/ir_model_view.xml @@ -86,6 +86,10 @@ + + + + @@ -169,7 +173,7 @@ - + @@ -193,6 +197,10 @@ + + + + diff --git a/openerp/models.py b/openerp/models.py index 69d37d88185..c6d3606d748 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -22,6 +22,7 @@ """ import datetime +import dateutil import functools import itertools import logging @@ -65,6 +66,19 @@ onchange_v7 = re.compile(r"^(\w+)\((.*)\)$") AUTOINIT_RECALCULATE_STORED_FIELDS = 1000 +# base environment for doing a safe eval +SAFE_EVAL_BASE = { + 'datetime': datetime, + 'dateutil': dateutil, + 'time': time, +} + +def make_compute(text, deps): + """ Return a compute function from its code body and dependencies. """ + func = lambda self: eval(text, SAFE_EVAL_BASE, {'self': self}, mode="exec") + deps = [arg.strip() for arg in (deps or "").split(",")] + return api.depends(*deps)(func) + def check_object_name(name): """ Check if the given name is a valid openerp object name. @@ -709,6 +723,9 @@ class BaseModel(object): attrs['column1'] = field['column1'] or col1 attrs['column2'] = field['column2'] or col2 attrs['domain'] = eval(field['domain']) if field['domain'] else None + # add compute function if given + if field['compute']: + attrs['compute'] = make_compute(field['compute'], field['depends']) self._add_field(name, Field.by_type[field['ttype']](**attrs)) @classmethod