Add ModelView.button_change
issue4397 review7781002
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Add button_change
|
||||
* Add support for PyPy
|
||||
* Add support for psycopg2cffi
|
||||
* Add noeval on PYSONDecoder
|
||||
|
||||
@@ -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
|
||||
<ref-models-fields-on_change>`).
|
||||
|
||||
.. warning::
|
||||
Only on instance methods.
|
||||
|
||||
Class methods:
|
||||
|
||||
.. classmethod:: ModelView.fields_view_get([view_id[, view_type[, toolbar]]])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user