From 158ebeb7a92de66b6f043795c0197cd188fdc90d Mon Sep 17 00:00:00 2001 From: Gael Pasgrimaud Date: Sat, 18 Jun 2011 10:22:02 +0200 Subject: [PATCH] improve actions. decorator and stuff --- pyramid_formalchemy/actions.py | 104 ++++++++++++++++++++++++++------- pyramid_formalchemy/views.py | 70 ++++++++++++++-------- 2 files changed, 128 insertions(+), 46 deletions(-) diff --git a/pyramid_formalchemy/actions.py b/pyramid_formalchemy/actions.py index 4a97454..2f96fc8 100644 --- a/pyramid_formalchemy/actions.py +++ b/pyramid_formalchemy/actions.py @@ -1,5 +1,25 @@ # -*- coding: utf-8 -*- from chameleon.zpt.template import PageTemplate +from pyramid.util import DottedNameResolver +import functools + +def action(name=None): + def wrapper(func): + action = name or func.__name__ + @functools.wraps(func) + def wrapped(self, *args, **kwargs): + request = self.request + if request.format in ('html', 'xhr') and request.model_class is not None: + for key in self.actions_categories: + attr = '%s_%s' % (action, key) + objects = getattr(request.model_class, attr, None) + if objects is None: + objects = self.defaults_actions.get(attr, Actions()) + setattr(request, key, objects) + request.action = func.__name__ + return func(self, *args, **kwargs) + return wrapped + return wrapper class Action(object): @@ -38,6 +58,9 @@ class Action(object): rcontext.update(self.rcontext) return self.template.render(**rcontext) + def __repr__(self): + return '<%s %s>' % (self.__class__.__name__, self.id) + class Link(Action): """ An action rendered as a link:: @@ -78,26 +101,27 @@ class UILink(Action): >>> from webob import Request >>> request = Request.blank('/') >>> action = UILink('myaction', icon='ui-icon-trash', - ... label='Click here') + ... label="string:Click here") >>> print action.render(request) - + Click here - + >>> action = UILink('myaction', icon='ui-icon-trash', - ... label='Click here', attrs={'onclick':'$(#link).click();'}) + ... label="'Click here'", attrs={'onclick':'$(#link).click();'}) >>> print action.render(request) - + Click here + """ body = ''' - + ''' def update(self): if 'state' not in self.rcontext: @@ -108,16 +132,58 @@ class UILink(Action): if 'href' not in self.attrs: self.attrs['href'] = repr('#') +class Actions(list): + """ + >>> actions = Actions('pyramid_formalchemy.actions.delete', + ... Link('link1', content=repr('A link'), attrs={'href':'request.application_url'})) + >>> actions + [, ] + + >>> from webob import Request + >>> request = Request.blank('/') + >>> print actions.render(request) #doctest: +ELLIPSIS + + + Delete + A link + + """ + + def __init__(self, *args): + res = DottedNameResolver('pyramid_formalchemy.actions') + list.__init__(self, [res.maybe_resolve(a) for a in args]) + + def render(self, request, **kwargs): + return u''.join([a.render(request, **kwargs) for a in self]) + + +new = UILink( + id='new', + label='string:New ${request.model_name}', + icon='ui-icon-circle-plus', + attrs=dict(href="request.fa_url(request.model_name, 'new')"), + ) + + save = UILink( id='save', - label='Save', + label='string:Save', icon='ui-icon-check', attrs=dict(onclick="jQuery(this).parents('form').submit();"), ) +save_and_add_another = UILink( + id='save_and_add_another', + label='string:Save and add another', + icon='ui-icon-check', + attrs=dict(onclick=("var f = jQuery(this).parents('form');" + "jQuery('#next', f).val(window.location.href);" + "f.submit();")), + ) + edit = UILink( id='edit', - label='Edit', + label='string:Edit', icon='ui-icon-check', attrs=dict(href="request.fa_url(request.model_name, request.model_id, 'edit')"), ) @@ -125,7 +191,7 @@ edit = UILink( delete = UILink( id='delete', views='edit', - label='Delete', + label='string:Delete', state='ui-state-error', icon='ui-icon-trash', attrs=dict(onclick=("string:var f = jQuery(this).parents('form');" @@ -136,20 +202,14 @@ delete = UILink( cancel = UILink( id='cancel', views='edit', - label='Cancel', + label='string:Cancel', icon='ui-icon-circle-arrow-w', attrs=dict(href="request.fa_url(request.model_name)"), ) -class Actions(list): - - def __init__(self, *args): - list.__init__(self, args) - - def render(self, request, **kwargs): - return u''.join([a.render(request, **kwargs) for a in self]) - -new_actions = Actions(save, cancel) -show_actions = Actions(edit, cancel) -edit_actions = Actions(save, delete, cancel) - +defaults_actions = dict( + listing_buttons=Actions(new), + new_buttons=Actions(save, save_and_add_another, cancel), + show_buttons=Actions(edit, cancel), + edit_buttons=Actions(save, delete, cancel), +) diff --git a/pyramid_formalchemy/views.py b/pyramid_formalchemy/views.py index ff68ca5..623970d 100644 --- a/pyramid_formalchemy/views.py +++ b/pyramid_formalchemy/views.py @@ -45,6 +45,9 @@ class ModelView(object): pager_args = dict(link_attr={'class': 'ui-pager-link ui-state-default ui-corner-all'}, curpage_attr={'class': 'ui-pager-curpage ui-state-highlight ui-corner-all'}) + actions_categories = ('buttons',) + defaults_actions = actions.defaults_actions + def __init__(self, context, request): self.context = context self.request = request @@ -118,8 +121,6 @@ class ModelView(object): def render(self, **kwargs): """render the form as html or json""" request = self.request - if 'action' in kwargs and request.format != 'json': - kwargs['actions'] = getattr(actions, '%(action)s_actions' % kwargs) if request.format != 'html': meth = getattr(self, 'render_%s_format' % request.format, None) if meth is not None: @@ -244,6 +245,7 @@ class ModelView(object): grid.append(Field('delete', fatypes.String, delete_link())) grid.readonly = True + @actions.action() def listing(self, **kwargs): """listing page""" page = self.get_page(**kwargs) @@ -274,10 +276,38 @@ class ModelView(object): pager = kwargs.pop('pager') return self.render_grid(fs=fs, id=None, pager=pager) + @actions.action() + def show(self): + id = self.request.model_id + fs = self.get_fieldset(suffix='View', id=id) + fs.readonly = True + + event = events.BeforeRenderEvent(self.request.model_instance, self.request, fs=fs) + alsoProvides(event, events.IBeforeShowRenderEvent) + zope.component.event.objectEventNotify(event) + + return self.render(fs=fs, id=id) + + @actions.action() + def new(self): + fs = self.get_fieldset(suffix='Add') + fs = fs.bind(session=self.session, request=self.request) + + event = events.BeforeRenderEvent(fs.model, self.request, fs=fs) + alsoProvides(event, events.IBeforeEditRenderEvent) + zope.component.event.objectEventNotify(event) + + return self.render(fs=fs, id=None) + + @actions.action('new') def create(self): request = self.request fs = self.get_fieldset(suffix='Add') + event = events.BeforeRenderEvent(fs.model, self.request, fs=fs) + alsoProvides(event, events.IBeforeEditRenderEvent) + zope.component.event.objectEventNotify(event) + if request.format == 'json' and request.method == 'PUT': data = json.load(request.body_file) else: @@ -288,6 +318,7 @@ class ModelView(object): except Exception: # non SA forms fs = fs.bind(self.context.get_model(), data=data, session=self.session, request=request) + if self.validate(fs): fs.sync() self.sync(fs) @@ -301,25 +332,10 @@ class ModelView(object): else: fs.rebind(fs.model, data=None) return self.render(fs=fs) - return self.render(fs=fs, action='new', id=None) + return self.render(fs=fs, id=None) - def show(self): - id = self.request.model_id - fs = self.get_fieldset(suffix='View', id=id) - fs.readonly = True - - event = events.BeforeRenderEvent(self.request.model_instance, self.request, fs=fs) - alsoProvides(event, events.IBeforeShowRenderEvent) - zope.component.event.objectEventNotify(event) - - return self.render(fs=fs, action='show', id=id) - - def new(self, **kwargs): - fs = self.get_fieldset(suffix='Add') - fs = fs.bind(session=self.session, request=self.request) - return self.render(fs=fs, action='new', id=None) - - def edit(self, id=None, **kwargs): + @actions.action() + def edit(self): id = self.request.model_id fs = self.get_fieldset(suffix='Edit', id=id) @@ -327,12 +343,18 @@ class ModelView(object): alsoProvides(event, events.IBeforeEditRenderEvent) zope.component.event.objectEventNotify(event) - return self.render(fs=fs, action='edit', id=id) + return self.render(fs=fs, id=id) - def update(self, **kwargs): + @actions.action('edit') + def update(self): request = self.request id = request.model_id fs = self.get_fieldset(suffix='Edit', id=id) + + event = events.BeforeRenderEvent(self.request.model_instance, self.request, fs=fs) + alsoProvides(event, events.IBeforeEditRenderEvent) + zope.component.event.objectEventNotify(event) + fs = fs.bind(request=request) if self.validate(fs): fs.sync() @@ -346,11 +368,11 @@ class ModelView(object): else: return self.render(fs=fs, status=0) if request.format == 'html': - return self.render(fs=fs, action='edit', id=id) + return self.render(fs=fs, id=id) else: return self.render(fs=fs, status=1) - def delete(self, **kwargs): + def delete(self): request = self.request record = request.model_instance