Skip to content

Commit

Permalink
base_exception: Remove side effect from api.constrains
Browse files Browse the repository at this point in the history
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#1642
  • Loading branch information
guewen authored and gfcapalbo committed Apr 13, 2022
1 parent 1e2cea9 commit bb141e7
Showing 1 changed file with 33 additions and 7 deletions.
40 changes: 33 additions & 7 deletions base_exception/models/base_exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,40 @@ def _get_popup_action(self):
@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:
Expand Down

0 comments on commit bb141e7

Please sign in to comment.