Add ButtonActionException and RPCReturnException
The RPCReturnException returns the result method instead of raising an exception. The ButtonActionException launch an action instead of executing the button. Closes #12009
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Add ButtonActionException and RPCReturnException
|
||||
* Rename ModelStorage.count into ModelStorage.estimated_count
|
||||
* Add border type to images
|
||||
* Lock table on transaction start
|
||||
|
||||
@@ -46,6 +46,13 @@ Exceptions
|
||||
|
||||
The exception raised when trying to execute a button without the rights.
|
||||
|
||||
.. exception:: ButtonActionException
|
||||
|
||||
The exception raised to launch the ``action`` instead of executing the
|
||||
button method.
|
||||
|
||||
The ``value`` attribute is the action value returned.
|
||||
|
||||
.. exception:: ImportDataError
|
||||
|
||||
The exception raises when importing data fails.
|
||||
|
||||
@@ -61,3 +61,14 @@ Instance methods are:
|
||||
.. method:: RCP.headers
|
||||
|
||||
Return a dictionary of the headers.
|
||||
|
||||
Exceptions
|
||||
==========
|
||||
|
||||
.. exception:: RPCReturnException
|
||||
|
||||
The base class of exceptions to return the result of ``result`` method
|
||||
instead of raising an exception.
|
||||
|
||||
The :class:`~trytond.transaction.Transaction` is rollbacked and tasks are
|
||||
cleared.
|
||||
|
||||
@@ -7,12 +7,13 @@ from .modelstorage import (
|
||||
ForbiddenCharValidationError, ImportDataError, RequiredValidationError,
|
||||
SelectionValidationError, SizeValidationError, TimeFormatValidationError,
|
||||
ValidationError)
|
||||
from .modelview import AccessButtonError
|
||||
from .modelview import AccessButtonError, ButtonActionException
|
||||
from .tree import RecursionError
|
||||
|
||||
__all__ = [
|
||||
AccessButtonError,
|
||||
AccessError,
|
||||
ButtonActionException,
|
||||
DigitsValidationError,
|
||||
DomainValidationError,
|
||||
ForeignKeyError,
|
||||
|
||||
@@ -10,7 +10,7 @@ from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import PYSONEncoder
|
||||
from trytond.rpc import RPC
|
||||
from trytond.rpc import RPC, RPCReturnException
|
||||
from trytond.tools import is_instance_method
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
@@ -25,6 +25,26 @@ class AccessButtonError(UserError):
|
||||
pass
|
||||
|
||||
|
||||
class ButtonActionException(RPCReturnException):
|
||||
"Exception to launch action instead of executing button."
|
||||
|
||||
def __init__(self, action, value=None):
|
||||
super().__init__()
|
||||
pool = Pool()
|
||||
ModelData = pool.get('ir.model.data')
|
||||
Action = pool.get('ir.action')
|
||||
|
||||
module, fs_id = action.split('.')
|
||||
action_id = Action.get_action_id(
|
||||
ModelData.get_id(module, fs_id))
|
||||
self.value = Action(action_id).get_action_value()
|
||||
if value:
|
||||
self.value.update(value)
|
||||
|
||||
def result(self):
|
||||
return self.value
|
||||
|
||||
|
||||
def on_change(func):
|
||||
@wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
|
||||
@@ -12,6 +12,7 @@ from trytond.config import config, get_hostname
|
||||
from trytond.exceptions import (
|
||||
ConcurrencyException, LoginException, RateLimitException, UserError,
|
||||
UserWarning)
|
||||
from trytond.rpc import RPCReturnException
|
||||
from trytond.tools import is_instance_method
|
||||
from trytond.transaction import Transaction, TransactionError
|
||||
from trytond.worker import run_task
|
||||
@@ -221,6 +222,10 @@ def _dispatch(request, pool, *args, **kwargs):
|
||||
continue
|
||||
logger.exception(log_message, *log_args, duration())
|
||||
raise
|
||||
except RPCReturnException as e:
|
||||
transaction.rollback()
|
||||
transaction.tasks.clear()
|
||||
result = e.result()
|
||||
except (ConcurrencyException, UserError, UserWarning,
|
||||
LoginException):
|
||||
logger.info(
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
import copy
|
||||
import datetime as dt
|
||||
|
||||
from trytond.exceptions import TrytonException
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
__all__ = ['RPC']
|
||||
|
||||
|
||||
class RPC(object):
|
||||
'''Define RPC behavior
|
||||
@@ -95,3 +94,10 @@ class RPCCache:
|
||||
return {
|
||||
'X-Tryton-Cache': int(self.duration.total_seconds()),
|
||||
}
|
||||
|
||||
|
||||
class RPCReturnException(TrytonException):
|
||||
"Exception to return response instead of being raised"
|
||||
|
||||
def result(self):
|
||||
pass
|
||||
|
||||
@@ -6,7 +6,8 @@ from unittest.mock import patch
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from trytond.model.exceptions import AccessButtonError, AccessError
|
||||
from trytond.model.exceptions import (
|
||||
AccessButtonError, AccessError, ButtonActionException)
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, PYSONDecoder, PYSONEncoder
|
||||
from trytond.tests.test_tryton import activate_module, with_transaction
|
||||
@@ -689,3 +690,27 @@ class ModelView(unittest.TestCase):
|
||||
self.assertIn('bar', writeable_fields)
|
||||
self.assertIn('baz', writeable_fields)
|
||||
self.assertIn('quux', writeable_fields)
|
||||
|
||||
@with_transaction()
|
||||
def test_button_action_exception(self):
|
||||
"Test value of ButtonActionException"
|
||||
exception = ButtonActionException('tests.test_modelview_button_action')
|
||||
self.assertEqual(exception.value, {
|
||||
**exception.value,
|
||||
'name': "Test Button Action",
|
||||
'url': 'http://www.example.com/',
|
||||
})
|
||||
|
||||
@with_transaction()
|
||||
def test_button_action_exception_value(self):
|
||||
"Test value of ButtonActionException"
|
||||
exception = ButtonActionException(
|
||||
'tests.test_modelview_button_action',
|
||||
value={
|
||||
'url': 'https://www.example.net',
|
||||
})
|
||||
self.assertEqual(exception.value, {
|
||||
**exception.value,
|
||||
'name': "Test Button Action",
|
||||
'url': 'https://www.example.net',
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user