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