improve actions. decorator and stuff

This commit is contained in:
Gael Pasgrimaud
2011-06-18 10:22:02 +02:00
parent 18c6a9e200
commit 158ebeb7a9
2 changed files with 128 additions and 46 deletions
+82 -22
View File
@@ -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)
<a class="ui-widget-header ui-widget-link ui-corner-all " id="myaction">
<a class="ui-widget-header ui-widget-link ui-widget-button ui-corner-all " id="myaction">
<span class="ui-icon ui-icon-trash"></span>
Click here
</a>
>>> 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)
<a class="ui-widget-header ui-widget-link ui-corner-all " id="myaction" onclick="$(#link).click();">
<a class="ui-widget-header ui-widget-link ui-widget-button ui-corner-all " href="#" id="myaction" onclick="$(#link).click();">
<span class="ui-icon ui-icon-trash"></span>
Click here
</a>
"""
body = '''
<a class="ui-widget-header ui-widget-link ui-widget-button ui-corner-all ${state}"
tal:attributes="%(attributes)s">
<span class="ui-icon ${icon}"></span>
<span tal:replace="label"></span>
<span tal:replace="%(label)s"></span>
</a>'''
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
[<UILink delete>, <Link link1>]
>>> from webob import Request
>>> request = Request.blank('/')
>>> print actions.render(request) #doctest: +ELLIPSIS
<a class="ui-widget-header ...">
<span class="ui-icon ui-icon-trash"></span>
Delete
</a><a href="http://localhost" id="link1">A link</a>
"""
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),
)
+46 -24
View File
@@ -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