From ad5eb3ed9774c44c111a53b108e15fac7c181ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 16 Mar 2015 12:56:11 +0100 Subject: [PATCH] Add ModelView.button_change issue4397 review7781002 --- CHANGELOG | 1 + doc/ref/models/models.rst | 9 +++++++ trytond/model/modelview.py | 48 ++++++++++++++++++++++++++++++--- trytond/protocols/dispatcher.py | 6 ----- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bae9d6764a..a7566c6824 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add button_change * Add support for PyPy * Add support for psycopg2cffi * Add noeval on PYSONDecoder diff --git a/doc/ref/models/models.rst b/doc/ref/models/models.rst index 1675caf5cb..26722d461a 100644 --- a/doc/ref/models/models.rst +++ b/doc/ref/models/models.rst @@ -129,6 +129,15 @@ Static methods: Same as :meth:`ModelView.button` but return the action id of the XML `id` action. +.. staticmethod:: ModelView.button_change([\*fields]) + + Same as :meth:`ModelView.button` but for button that change values of the + fields on client side (similar to :ref:`on_change + `). + + .. warning:: + Only on instance methods. + Class methods: .. classmethod:: ModelView.fields_view_get([view_id[, view_type[, toolbar]]]) diff --git a/trytond/model/modelview.py b/trytond/model/modelview.py index 4c4ce8c08d..e8b260ac7a 100644 --- a/trytond/model/modelview.py +++ b/trytond/model/modelview.py @@ -125,10 +125,12 @@ class ModelView(Model): else: cls.__depend_methods = collections.defaultdict(set) - for field_name in dir(cls): - field = getattr(cls, field_name) - if not isinstance(field, fields.Field): - continue + if hasattr(cls, '__change_buttons'): + cls.__change_buttons = cls.__change_buttons.copy() + else: + cls.__change_buttons = collections.defaultdict(set) + + def setup_field(field, field_name): for attribute in ('on_change', 'on_change_with', 'autocomplete', 'selection_change_with'): if attribute == 'selection_change_with': @@ -159,6 +161,17 @@ class ModelView(Model): # Decorate on_change to always return self setattr(cls, function_name, on_change(function)) + def setup_callable(function, name): + if hasattr(function, 'change'): + cls.__change_buttons[name] |= function.change + + for name in dir(cls): + attr = getattr(cls, name) + if isinstance(attr, fields.Field): + setup_field(attr, name) + elif isinstance(attr, collections.Callable): + setup_callable(attr, name) + @classmethod def __post_setup__(cls): super(ModelView, cls).__post_setup__() @@ -181,6 +194,15 @@ class ModelView(Model): cls.__rpc__.setdefault(function_name, RPC(instantiate=0, result=result)) + for button in cls._buttons: + method = getattr(cls, button) + if not hasattr(method, 'im_self') or method.im_self: + cls.__rpc__.setdefault(button, + RPC(readonly=False, instantiate=0)) + else: + cls.__rpc__.setdefault(button, + RPC(instantiate=0, result=on_change_result)) + # Update depend on methods for (field_name, attribute), others in ( cls.__depend_methods.iteritems()): @@ -540,6 +562,15 @@ class ModelView(Model): states['readonly'] = True element.set('states', encoder.encode(states)) + change = cls.__change_buttons[button_name] + if change: + element.set('change', encoder.encode(list(change))) + method = getattr(cls, button_name) + if not hasattr(method, 'im_self') or method.im_self: + element.set('type', 'class') + else: + element.set('type', 'instance') + # translate view if Transaction().language != 'en_US': for attr in ('string', 'sum', 'confirm', 'help'): @@ -611,6 +642,15 @@ class ModelView(Model): return wrapper return decorator + @staticmethod + def button_change(*fields): + def decorator(func): + func = ModelView.button(func) + func = on_change(func) + func.change = set(fields) + return func + return decorator + def on_change(self, fieldnames): for fieldname in sorted(fieldnames): method = getattr(self, 'on_change_%s' % fieldname, None) diff --git a/trytond/protocols/dispatcher.py b/trytond/protocols/dispatcher.py index c78f469bab..cb0d84efc0 100644 --- a/trytond/protocols/dispatcher.py +++ b/trytond/protocols/dispatcher.py @@ -16,7 +16,6 @@ from trytond.transaction import Transaction from trytond.cache import Cache from trytond.exceptions import UserError, UserWarning, NotLogged, \ ConcurrencyException -from trytond.rpc import RPC logger = logging.getLogger(__name__) @@ -105,9 +104,6 @@ def dispatch(host, port, protocol, database_name, user, session, object_type, for object_name, obj in pool.iterobject(type=type): for method in obj.__rpc__: res.append(type + '.' + object_name + '.' + method) - if hasattr(obj, '_buttons'): - for button in obj._buttons: - res.append(type + '.' + object_name + '.' + button) return res elif method == 'methodSignature': return 'signatures not supported' @@ -139,8 +135,6 @@ def dispatch(host, port, protocol, database_name, user, session, object_type, if method in obj.__rpc__: rpc = obj.__rpc__[method] - elif method in getattr(obj, '_buttons', {}): - rpc = RPC(readonly=False, instantiate=0) else: raise UserError('Calling method %s on %s %s is not allowed!' % (method, object_type, object_name))