diff --git a/openerp/addons/test_new_api/__openerp__.py b/openerp/addons/test_new_api/__openerp__.py index 9c859de4445..89355ac19ee 100644 --- a/openerp/addons/test_new_api/__openerp__.py +++ b/openerp/addons/test_new_api/__openerp__.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- { - 'name': 'Test New API', + 'name': 'Test API', 'version': '1.0', 'category': 'Tests', - 'description': """A module to test the new API.""", + 'description': """A module to test the API.""", 'author': 'OpenERP SA', 'maintainer': 'OpenERP SA', 'website': 'http://www.openerp.com', diff --git a/openerp/addons/test_new_api/demo_data.xml b/openerp/addons/test_new_api/demo_data.xml index acb53a7b07f..1715b024cd7 100644 --- a/openerp/addons/test_new_api/demo_data.xml +++ b/openerp/addons/test_new_api/demo_data.xml @@ -26,5 +26,7 @@ This is a much longer message + + diff --git a/openerp/addons/test_new_api/ir.model.access.csv b/openerp/addons/test_new_api/ir.model.access.csv index 5b62045ae08..2bbd1adf12d 100644 --- a/openerp/addons/test_new_api/ir.model.access.csv +++ b/openerp/addons/test_new_api/ir.model.access.csv @@ -3,3 +3,5 @@ access_category,test_new_api_category,test_new_api.model_test_new_api_category,, access_discussion,test_new_api_discussion,test_new_api.model_test_new_api_discussion,,1,1,1,1 access_message,test_new_api_message,test_new_api.model_test_new_api_message,,1,1,1,1 access_mixed,test_new_api_mixed,test_new_api.model_test_new_api_mixed,,1,1,1,1 +access_test_function_noinfiniterecursion,access_test_function_noinfiniterecursion,model_test_old_api_function_noinfiniterecursion,,1,1,1,1 +access_test_function_counter,access_test_function_counter,model_test_old_api_function_counter,,1,1,1,1 diff --git a/openerp/addons/test_new_api/models.py b/openerp/addons/test_new_api/models.py index 4399cb0ed49..173935a8985 100644 --- a/openerp/addons/test_new_api/models.py +++ b/openerp/addons/test_new_api/models.py @@ -19,9 +19,17 @@ # ############################################################################## +import datetime from openerp.exceptions import AccessError + +############################################################################## +# +# OLD API +# +############################################################################## from openerp.osv import osv, fields + class res_partner(osv.Model): _inherit = 'res.partner' @@ -41,6 +49,48 @@ class res_partner(osv.Model): } +class TestFunctionCounter(osv.Model): + _name = 'test_old_api.function_counter' + + def _compute_cnt(self, cr, uid, ids, fname, arg, context=None): + res = {} + for cnt in self.browse(cr, uid, ids, context=context): + res[cnt.id] = cnt.access and cnt.cnt + 1 or 0 + return res + + _columns = { + 'access': fields.datetime('Datetime Field'), + 'cnt': fields.function( + _compute_cnt, type='integer', string='Function Field', store=True), + } + + +class TestFunctionNoInfiniteRecursion(osv.Model): + _name = 'test_old_api.function_noinfiniterecursion' + + def _compute_f1(self, cr, uid, ids, fname, arg, context=None): + res = {} + for tf in self.browse(cr, uid, ids, context=context): + res[tf.id] = 'create' in tf.f0 and 'create' or 'write' + cntobj = self.pool['test_old_api.function_counter'] + cnt_id = self.pool['ir.model.data'].xmlid_to_res_id( + cr, uid, 'test_new_api.c1') + cntobj.write( + cr, uid, cnt_id, {'access': datetime.datetime.now()}, + context=context) + return res + + _columns = { + 'f0': fields.char('Char Field'), + 'f1': fields.function( + _compute_f1, type='char', string='Function Field', store=True), + } + +############################################################################## +# +# NEW API +# +############################################################################## from openerp import models, fields, api, _ diff --git a/openerp/addons/test_new_api/tests/__init__.py b/openerp/addons/test_new_api/tests/__init__.py index 790ad9c9a59..f4803bd4837 100644 --- a/openerp/addons/test_new_api/tests/__init__.py +++ b/openerp/addons/test_new_api/tests/__init__.py @@ -5,3 +5,4 @@ from . import test_new_fields from . import test_onchange from . import test_field_conversions from . import test_attributes +from . import test_no_infinite_recursion diff --git a/openerp/addons/test_new_api/tests/test_no_infinite_recursion.py b/openerp/addons/test_new_api/tests/test_no_infinite_recursion.py new file mode 100644 index 00000000000..68ef3fcf3b9 --- /dev/null +++ b/openerp/addons/test_new_api/tests/test_no_infinite_recursion.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from openerp.tests import common + + +class test_no_infinite_recursion(common.TransactionCase): + + def setUp(self): + super(test_no_infinite_recursion, self).setUp() + self.tstfct = self.registry['test_old_api.function_noinfiniterecursion'] + + def test_00_create_and_update(self): + """ + Check that computing old api function field does not cycle infinitely + See https://github.com/odoo/odoo/pull/7558 + """ + cr, uid, context, tstfct = self.cr, self.uid, {}, self.tstfct + + vals = { + 'f0': 'Test create', + } + idnew = tstfct.create(cr, uid, vals, context=context) + tst = tstfct.browse(cr, uid, idnew, context=context) + + self.assertEqual(tst.f1, 'create') + + vals = { + 'f0': 'Test write', + } + tstfct.write(cr, uid, idnew, vals, context=context) + + self.assertEqual(tst.f1, 'write') diff --git a/openerp/models.py b/openerp/models.py index 35944ff4873..b0c7762bbd7 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -4007,18 +4007,22 @@ class BaseModel(object): done = {} recs.env.recompute_old.extend(result) - for order, model_name, ids_to_update, fields_to_recompute in sorted(recs.env.recompute_old): - key = (model_name, tuple(fields_to_recompute)) - done.setdefault(key, {}) - # avoid to do several times the same computation - todo = [] - for id in ids_to_update: - if id not in done[key]: - done[key][id] = True - if id not in deleted_related[model_name]: - todo.append(id) - self.pool[model_name]._store_set_values(cr, user, todo, fields_to_recompute, context) - recs.env.clear_recompute_old() + while recs.env.recompute_old: + sorted_recompute_old = sorted(recs.env.recompute_old) + recs.env.clear_recompute_old() + for __, model_name, ids_to_update, fields_to_recompute in \ + sorted_recompute_old: + key = (model_name, tuple(fields_to_recompute)) + done.setdefault(key, {}) + # avoid to do several times the same computation + todo = [] + for id in ids_to_update: + if id not in done[key]: + done[key][id] = True + if id not in deleted_related[model_name]: + todo.append(id) + self.pool[model_name]._store_set_values( + cr, user, todo, fields_to_recompute, context) # recompute new-style fields if recs.env.recompute and context.get('recompute', True): @@ -4275,11 +4279,15 @@ class BaseModel(object): if recs.env.recompute and context.get('recompute', True): done = [] - for order, model_name, ids, fields2 in sorted(recs.env.recompute_old): - if not (model_name, ids, fields2) in done: - self.pool[model_name]._store_set_values(cr, user, ids, fields2, context) - done.append((model_name, ids, fields2)) - recs.env.clear_recompute_old() + while recs.env.recompute_old: + sorted_recompute_old = sorted(recs.env.recompute_old) + recs.env.clear_recompute_old() + for __, model_name, ids, fields2 in sorted_recompute_old: + if not (model_name, ids, fields2) in done: + self.pool[model_name]._store_set_values( + cr, user, ids, fields2, context) + done.append((model_name, ids, fields2)) + # recompute new-style fields recs.recompute()