[FIX] models: old api, prevent infinite recursion in stored function fields
The risk was introduced byb7f1b9c
. IF _store_set_values() recall another _create() or _write(), the recomputation mechanism enter in an infinite recursion trying to reevaluate for each call exactly the same fields for the same records than the previous one This revision replaces the loop of _store_set_values() by 2 nested loops: - that not breaks the entire consumption of recompute_old queue (Tested thanks to revisiona922d39
), - that allows to clear the queue before each recomputations bundle fixing thereby the recursion Closes #7558
This commit is contained in:
parent
408ac6a5f0
commit
017688cc29
|
@ -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',
|
||||
|
|
|
@ -26,5 +26,7 @@
|
|||
<field name="discussion" ref="discussion_0"/>
|
||||
<field name="body">This is a much longer message</field>
|
||||
</record>
|
||||
<record id="c1" model="test_old_api.function_counter">
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -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
|
||||
|
|
|
|
@ -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, _
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
|
@ -4007,7 +4007,11 @@ 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):
|
||||
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
|
||||
|
@ -4017,8 +4021,8 @@ class BaseModel(object):
|
|||
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()
|
||||
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))
|
||||
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()
|
||||
|
||||
|
|
Loading…
Reference in New Issue