Browse Source

base_exception: Remove side effect from api.constrains

In the documentation.

The method called by '_check_exception' has a side effect, it writes
on 'exception.rule' + on the Many2many relation between it and
the related model (such as sale.order). When decorated by
@api.constrains, any error during the method will be caught and
re-raised as "ValidationError".  This part of code is very prone to
concurrent updates as 2 sales having the same exception will both write
on the same 'exception.rule'.  A concurrent update (OperationalError) is
re-raised as ValidationError, and then is not retried properly.

Calling the same method in create/write has the same effect than
@api.constrains without shadowing the exception type.

Full explanation:
OCA/server-tools#1642
10.0
Guewen Baconnier 5 years ago
parent
commit
2750e15062
  1. 40
      base_exception/models/base_exception.py

40
base_exception/models/base_exception.py

@ -237,14 +237,40 @@ class BaseException(models.AbstractModel):
@api.multi
def _check_exception(self):
"""
This method must be used in a constraint that must be created in the
object that inherits for base.exception.
for sale :
@api.constrains('ignore_exception',)
This method must be called in the create and write methods of the model
using exceptions. The model has to inherit from 'base.exception'.
Example for sale (used in OCA/sale-workflow/sale_exception):
def _fields_trigger_check_exception(self):
return ['ignore_exception', 'order_line', 'state']
@api.model
def create(self, vals):
record = super(SaleOrder, self).create(vals)
check_exceptions = any(
field in vals for field
in self._fields_trigger_check_exception()
)
if check_exceptions:
record.sale_check_exception()
return record
@api.multi
def write(self, vals):
result = super(SaleOrder, self).write(vals)
check_exceptions = any(
field in vals for field
in self._fields_trigger_check_exception()
)
if check_exceptions:
self.sale_check_exception()
return result
def sale_check_exception(self):
...
...
self._check_exception
orders = self.filtered(lambda s: s.state == 'sale')
if orders:
orders._check_exception()
"""
exception_ids = self.detect_exceptions()
if exception_ids:

Loading…
Cancel
Save