convenience commit (form refactoring)
This commit is contained in:
parent
dd3b42d981
commit
f0c8858fb0
19 changed files with 736 additions and 184 deletions
|
@ -44,7 +44,7 @@ inited = False
|
||||||
engines = None
|
engines = None
|
||||||
engine = None
|
engine = None
|
||||||
Session = sessionmaker()
|
Session = sessionmaker()
|
||||||
Base = declarative_base()
|
Base = declarative_base(cls=edbob.Object)
|
||||||
# metadata = None
|
# metadata = None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,4 +26,6 @@
|
||||||
``edbob.pyramid.forms`` -- Forms
|
``edbob.pyramid.forms`` -- Forms
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from edbob.pyramid.forms.core import *
|
||||||
from edbob.pyramid.forms.formalchemy import *
|
from edbob.pyramid.forms.formalchemy import *
|
||||||
|
from edbob.pyramid.forms.simpleform import *
|
||||||
|
|
93
edbob/pyramid/forms/core.py
Normal file
93
edbob/pyramid/forms/core.py
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# edbob -- Pythonic Software Framework
|
||||||
|
# Copyright © 2010-2012 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of edbob.
|
||||||
|
#
|
||||||
|
# edbob is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# edbob is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with edbob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
``edbob.pyramid.forms.core`` -- Core Forms
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sqlalchemy.util import OrderedDict
|
||||||
|
|
||||||
|
from webhelpers.html import literal, tags
|
||||||
|
|
||||||
|
import edbob
|
||||||
|
from edbob.util import requires_impl
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['Form']
|
||||||
|
|
||||||
|
|
||||||
|
class Form(edbob.Object):
|
||||||
|
"""
|
||||||
|
Generic form class.
|
||||||
|
|
||||||
|
This class exists primarily so that rendering calls may mimic those used by
|
||||||
|
FormAlchemy.
|
||||||
|
"""
|
||||||
|
|
||||||
|
readonly = False
|
||||||
|
successive = False
|
||||||
|
|
||||||
|
action_url = None
|
||||||
|
home_route = None
|
||||||
|
home_url = None
|
||||||
|
# template = None
|
||||||
|
|
||||||
|
render_fields = OrderedDict()
|
||||||
|
errors = {}
|
||||||
|
|
||||||
|
# def __init__(self, request=None, action_url=None, home_url=None, template=None, **kwargs):
|
||||||
|
def __init__(self, request=None, action_url=None, home_url=None, **kwargs):
|
||||||
|
super(Form, self).__init__(**kwargs)
|
||||||
|
self.request = request
|
||||||
|
if action_url:
|
||||||
|
self.action_url = action_url
|
||||||
|
if request and not self.action_url:
|
||||||
|
self.action_url = request.current_route_url()
|
||||||
|
if home_url:
|
||||||
|
self.home_url = home_url
|
||||||
|
if request and not self.home_url:
|
||||||
|
home = self.home_route if self.home_route else 'home'
|
||||||
|
self.home_url = request.route_url(home)
|
||||||
|
# if template:
|
||||||
|
# self.template = template
|
||||||
|
# if not self.template:
|
||||||
|
# self.template = '%s.mako' % self.action_url
|
||||||
|
|
||||||
|
@property
|
||||||
|
def action_url(self):
|
||||||
|
return self.request.current_route_url()
|
||||||
|
|
||||||
|
def standard_buttons(self, submit="Save"):
|
||||||
|
return literal(tags.submit('submit', submit) + ' ' + self.cancel_button())
|
||||||
|
|
||||||
|
def cancel_button(self):
|
||||||
|
return literal('<button type="button" class="cancel">Cancel</button>')
|
||||||
|
|
||||||
|
def render(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Renders the form as HTML. All keyword arguments are passed on to the
|
||||||
|
template context.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return ''
|
|
@ -44,12 +44,14 @@ from formalchemy.validators import accepts_none
|
||||||
import edbob
|
import edbob
|
||||||
from edbob.lib import pretty
|
from edbob.lib import pretty
|
||||||
from edbob.util import prettify
|
from edbob.util import prettify
|
||||||
from edbob.pyramid import Session
|
from edbob.pyramid import Session, helpers
|
||||||
|
|
||||||
|
from edbob.pyramid.forms.formalchemy.fieldset import *
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['AlchemyGrid', 'ChildGridField', 'PropertyField',
|
__all__ = ['AlchemyGrid', 'ChildGridField', 'PropertyField',
|
||||||
'EnumFieldRenderer', 'PrettyDateTimeFieldRenderer',
|
'EnumFieldRenderer', 'PrettyDateTimeFieldRenderer',
|
||||||
'AutocompleteFieldRenderer',
|
'AutocompleteFieldRenderer', 'FieldSet',
|
||||||
'make_fieldset', 'required', 'pretty_datetime']
|
'make_fieldset', 'required', 'pretty_datetime']
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,33 +70,6 @@ engine = TemplateEngine()
|
||||||
formalchemy.config.engine = engine
|
formalchemy.config.engine = engine
|
||||||
|
|
||||||
|
|
||||||
class FieldSet(formalchemy.FieldSet):
|
|
||||||
"""
|
|
||||||
Adds a little magic to the ``FieldSet`` class.
|
|
||||||
"""
|
|
||||||
|
|
||||||
prettify = staticmethod(prettify)
|
|
||||||
|
|
||||||
def __init__(self, model, class_name=None, crud_title=None, url=None,
|
|
||||||
route_name=None, action_url='', list_url=None, cancel_url=None, **kwargs):
|
|
||||||
super(FieldSet, self).__init__(model, **kwargs)
|
|
||||||
self.class_name = class_name or self._original_cls.__name__.lower()
|
|
||||||
self.crud_title = crud_title or prettify(self.class_name)
|
|
||||||
self.edit = isinstance(model, self._original_cls)
|
|
||||||
self.route_name = route_name or (self.class_name + 's')
|
|
||||||
self.action_url = action_url
|
|
||||||
self.list_url = list_url
|
|
||||||
self.cancel_url = cancel_url or self.list_url
|
|
||||||
self.allow_continue = kwargs.pop('allow_continue', False)
|
|
||||||
|
|
||||||
def get_display_text(self):
|
|
||||||
return unicode(self.model)
|
|
||||||
|
|
||||||
def render(self, **kwargs):
|
|
||||||
kwargs.setdefault('class_', self.class_name)
|
|
||||||
return formalchemy.FieldSet.render(self, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class AlchemyGrid(formalchemy.Grid):
|
class AlchemyGrid(formalchemy.Grid):
|
||||||
"""
|
"""
|
||||||
This class defines the basic grid which you see in pretty much all
|
This class defines the basic grid which you see in pretty much all
|
||||||
|
@ -308,11 +283,6 @@ class PropertyField(formalchemy.Field):
|
||||||
self.set(readonly=True)
|
self.set(readonly=True)
|
||||||
|
|
||||||
|
|
||||||
def make_fieldset(model, **kwargs):
|
|
||||||
kwargs.setdefault('session', Session())
|
|
||||||
return FieldSet(model, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@accepts_none
|
@accepts_none
|
||||||
def required(value, field=None):
|
def required(value, field=None):
|
||||||
if value is None or value == '':
|
if value is None or value == '':
|
||||||
|
@ -371,6 +341,23 @@ class PrettyDateTimeFieldRenderer(formalchemy.fields.DateTimeFieldRenderer):
|
||||||
return pretty_datetime(self.raw_value)
|
return pretty_datetime(self.raw_value)
|
||||||
|
|
||||||
|
|
||||||
|
class DateTimeFieldRenderer(formalchemy.fields.DateTimeFieldRenderer):
|
||||||
|
"""
|
||||||
|
Leverages edbob time system to coerce timestamp to local time zone before
|
||||||
|
displaying it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def render_readonly(self, **kwargs):
|
||||||
|
value = self.raw_value
|
||||||
|
if isinstance(value, datetime.datetime):
|
||||||
|
value = edbob.local_time(value)
|
||||||
|
return value.strftime(self.format)
|
||||||
|
print type(value)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
FieldSet.default_renderers[formalchemy.types.DateTime] = DateTimeFieldRenderer
|
||||||
|
|
||||||
|
|
||||||
def AutocompleteFieldRenderer(service_url, display, width='300px', callback=None, **kwargs):
|
def AutocompleteFieldRenderer(service_url, display, width='300px', callback=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Returns a field renderer class which leverages jQuery autocomplete to
|
Returns a field renderer class which leverages jQuery autocomplete to
|
||||||
|
@ -421,7 +408,6 @@ class _AutocompleteFieldRenderer(fields.FieldRenderer):
|
||||||
autocompleter_name = 'autocompleter_%s' % self.name.replace('-', '_')
|
autocompleter_name = 'autocompleter_%s' % self.name.replace('-', '_')
|
||||||
return formalchemy.config.engine('field_autocomplete', fieldname=self.name,
|
return formalchemy.config.engine('field_autocomplete', fieldname=self.name,
|
||||||
fieldvalue=self.value, display=self._display(self.value),
|
fieldvalue=self.value, display=self._display(self.value),
|
||||||
autocompleter_name=autocompleter_name,
|
autocompleter=autocompleter_name,
|
||||||
service_url=self.service_url, width=self.width,
|
service_url=self.service_url, width=self.width,
|
||||||
callback=self.callback, hidden=tags.hidden, text=tags.text,
|
callback=self.callback, h=helpers, **kwargs)
|
||||||
**kwargs)
|
|
72
edbob/pyramid/forms/formalchemy/fieldset.py
Normal file
72
edbob/pyramid/forms/formalchemy/fieldset.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# edbob -- Pythonic Software Framework
|
||||||
|
# Copyright © 2010-2012 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of edbob.
|
||||||
|
#
|
||||||
|
# edbob is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# edbob is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with edbob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
``edbob.pyramid.forms.formalchemy.fieldset`` -- FormAlchemy FieldSet
|
||||||
|
"""
|
||||||
|
|
||||||
|
import formalchemy
|
||||||
|
|
||||||
|
from edbob.pyramid import Session
|
||||||
|
from edbob.util import prettify
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['FieldSet', 'make_fieldset']
|
||||||
|
|
||||||
|
|
||||||
|
class FieldSet(formalchemy.FieldSet):
|
||||||
|
"""
|
||||||
|
Adds a little magic to the :class:`formalchemy.FieldSet` class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
prettify = staticmethod(prettify)
|
||||||
|
|
||||||
|
def __init__(self, model, class_name=None, crud_title=None, url=None,
|
||||||
|
route_name=None, action_url='', home_url=None, **kwargs):
|
||||||
|
super(FieldSet, self).__init__(model, **kwargs)
|
||||||
|
self.class_name = class_name or self._original_cls.__name__.lower()
|
||||||
|
self.crud_title = crud_title or prettify(self.class_name)
|
||||||
|
self.edit = isinstance(model, self._original_cls)
|
||||||
|
self.route_name = route_name or (self.class_name + 's')
|
||||||
|
self.action_url = action_url
|
||||||
|
self.home_url = home_url
|
||||||
|
self.allow_continue = kwargs.pop('allow_continue', False)
|
||||||
|
|
||||||
|
def get_display_text(self):
|
||||||
|
return unicode(self.model)
|
||||||
|
|
||||||
|
def render(self, **kwargs):
|
||||||
|
kwargs.setdefault('class_', self.class_name)
|
||||||
|
return super(FieldSet, self).render(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def make_fieldset(model, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns a :class:`FieldSet` equipped with the current scoped
|
||||||
|
:class:`edbob.db.Session` instance (unless ``session`` is provided as a
|
||||||
|
keyword argument).
|
||||||
|
"""
|
||||||
|
|
||||||
|
kwargs.setdefault('session', Session())
|
||||||
|
return FieldSet(model, **kwargs)
|
53
edbob/pyramid/forms/simpleform.py
Normal file
53
edbob/pyramid/forms/simpleform.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# edbob -- Pythonic Software Framework
|
||||||
|
# Copyright © 2010-2012 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of edbob.
|
||||||
|
#
|
||||||
|
# edbob is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# edbob is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with edbob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
``edbob.pyramid.forms.simpleform`` -- pyramid_simpleform Forms
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pyramid_simpleform
|
||||||
|
from pyramid.renderers import render
|
||||||
|
from pyramid_simpleform.renderers import FormRenderer
|
||||||
|
|
||||||
|
from edbob.pyramid import helpers
|
||||||
|
from edbob.pyramid.forms import Form
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['SimpleForm']
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleForm(Form):
|
||||||
|
|
||||||
|
template = None
|
||||||
|
|
||||||
|
def __init__(self, request, **kwargs):
|
||||||
|
super(SimpleForm, self).__init__(request, **kwargs)
|
||||||
|
self.form = pyramid_simpleform.Form(request)
|
||||||
|
|
||||||
|
def render(self, **kwargs):
|
||||||
|
kw = {
|
||||||
|
'form': self,
|
||||||
|
}
|
||||||
|
kw.update(kwargs)
|
||||||
|
return render('/forms/form.mako', kw)
|
|
@ -1,7 +1,8 @@
|
||||||
<%def name="global_title()">edbob</%def>
|
<%def name="global_title()">edbob</%def>
|
||||||
<%def name="title()">${(fieldset.crud_title+' : '+fieldset.get_display_text() if fieldset.edit else 'New '+fieldset.crud_title) if crud else ''}</%def>
|
<%def name="title()"></%def>
|
||||||
<%def name="head_tags()"></%def>
|
<%def name="head_tags()"></%def>
|
||||||
<%def name="home_link()"><h1 class="right">${h.link_to("Home", url('home'))}</h1></%def>
|
<%def name="home_link()"><h1 class="right">${h.link_to("Home", url('home'))}</h1></%def>
|
||||||
|
<%def name="menu()"></%def>
|
||||||
<%def name="footer()">
|
<%def name="footer()">
|
||||||
powered by ${h.link_to('edbob', 'http://edbob.org', target='_blank')} v${edbob.__version__}
|
powered by ${h.link_to('edbob', 'http://edbob.org', target='_blank')} v${edbob.__version__}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<%inherit file="/base.mako" />
|
<%inherit file="/base.mako" />
|
||||||
|
|
||||||
|
<%def name="title()">${(fieldset.crud_title+' : '+fieldset.get_display_text() if fieldset.edit else 'New '+fieldset.crud_title) if crud else ''}</%def>
|
||||||
|
|
||||||
<div class="crud wrapper">
|
<div class="crud wrapper">
|
||||||
|
|
||||||
<div class="right">
|
<div class="right">
|
||||||
|
|
16
edbob/pyramid/templates/edbob/form.mako
Normal file
16
edbob/pyramid/templates/edbob/form.mako
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<%inherit file="/base.mako" />
|
||||||
|
|
||||||
|
<%def name="buttons()"></%def>
|
||||||
|
|
||||||
|
<div class="wrapper">
|
||||||
|
|
||||||
|
<div class="right">
|
||||||
|
${self.menu()}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="left">
|
||||||
|
<% print 'type (2) is', type(form) %>
|
||||||
|
${form.render(buttons=self.buttons)|n}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
58
edbob/pyramid/templates/edbob/form_body.mako
Normal file
58
edbob/pyramid/templates/edbob/form_body.mako
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<% _focus_rendered = False %>
|
||||||
|
|
||||||
|
<div class="form">
|
||||||
|
${h.form(form.action_url, enctype='multipart/form-data')}
|
||||||
|
|
||||||
|
% for error in form.errors.get(None, []):
|
||||||
|
<div class="fieldset-error">${error}</div>
|
||||||
|
% endfor
|
||||||
|
|
||||||
|
% for field in form.render_fields.itervalues():
|
||||||
|
|
||||||
|
<div class="field-couple ${field.name}">
|
||||||
|
% for error in field.errors:
|
||||||
|
<div class="field-error">${error}</div>
|
||||||
|
% endfor
|
||||||
|
${field.label_tag()|n}
|
||||||
|
<div class="field">
|
||||||
|
${field.render()|n}
|
||||||
|
</div>
|
||||||
|
% if 'instructions' in field.metadata:
|
||||||
|
<span class="instructions">${field.metadata['instructions']}</span>
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
% if (form.focus == field or form.focus is True) and not _focus_rendered:
|
||||||
|
% if not field.is_readonly():
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
$(function() {
|
||||||
|
$('#${field.renderer.name}').focus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<% _focus_rendered = True %>
|
||||||
|
% endif
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% endfor
|
||||||
|
|
||||||
|
% if form.successive:
|
||||||
|
<div class="checkbox">
|
||||||
|
${h.checkbox('keep-going', checked=True)}
|
||||||
|
<label for="keep-going">Add another after this one</label>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
${h.submit('submit', "Save")}
|
||||||
|
<button type="button" class="cancel">Cancel</button>
|
||||||
|
</div>
|
||||||
|
${h.end_form()}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
$(function() {
|
||||||
|
$('button.cancel').click(function() {
|
||||||
|
location.href = '${form.home_url}';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
2
edbob/pyramid/templates/form.mako
Normal file
2
edbob/pyramid/templates/form.mako
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<%inherit file="/edbob/form.mako" />
|
||||||
|
${parent.body()}
|
|
@ -1,6 +1,6 @@
|
||||||
<div id="${fieldname}-container" class="autocomplete-container">
|
<div id="${fieldname}-container" class="autocomplete-container">
|
||||||
${hidden(fieldname, id=fieldname, value=fieldvalue)}
|
${h.hidden(fieldname, id=fieldname, value=fieldvalue)}
|
||||||
${text(fieldname+'-textbox', id=fieldname+'-textbox', value=display,
|
${h.text(fieldname+'-textbox', id=fieldname+'-textbox', value=display,
|
||||||
class_='autocomplete-textbox', style='display: none;' if fieldvalue else '')}
|
class_='autocomplete-textbox', style='display: none;' if fieldvalue else '')}
|
||||||
<div id="${fieldname}-display" class="autocomplete-display"${'' if fieldvalue else ' style="display: none;"'|n}>
|
<div id="${fieldname}-display" class="autocomplete-display"${'' if fieldvalue else ' style="display: none;"'|n}>
|
||||||
<span>${display}</span>
|
<span>${display}</span>
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
</div>
|
</div>
|
||||||
<script language="javascript" type="text/javascript">
|
<script language="javascript" type="text/javascript">
|
||||||
$(function() {
|
$(function() {
|
||||||
var ${autocompleter_name} = $('#${fieldname}-textbox').autocomplete({
|
var ${autocompleter} = $('#${fieldname}-textbox').autocomplete({
|
||||||
serviceUrl: '${service_url}',
|
serviceUrl: '${service_url}',
|
||||||
width: '${width}',
|
width: '${width}',
|
||||||
onSelect: function(value, data) {
|
onSelect: function(value, data) {
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<script language="javascript" type="text/javascript">
|
<script language="javascript" type="text/javascript">
|
||||||
$(function() {
|
$(function() {
|
||||||
$('button.cancel').click(function() {
|
$('button.cancel').click(function() {
|
||||||
location.href = '${fieldset.cancel_url}';
|
location.href = '${fieldset.home_url}';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
62
edbob/pyramid/templates/forms/form.mako
Normal file
62
edbob/pyramid/templates/forms/form.mako
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<% _focus_rendered = False %>
|
||||||
|
|
||||||
|
<div class="form">
|
||||||
|
${h.form(form.action_url, enctype='multipart/form-data')}
|
||||||
|
|
||||||
|
% for error in form.errors.get(None, []):
|
||||||
|
<div class="fieldset-error">${error}</div>
|
||||||
|
% endfor
|
||||||
|
|
||||||
|
% for field in form.render_fields.itervalues():
|
||||||
|
|
||||||
|
<div class="field-couple ${field.name}">
|
||||||
|
% for error in field.errors:
|
||||||
|
<div class="field-error">${error}</div>
|
||||||
|
% endfor
|
||||||
|
${field.label_tag()|n}
|
||||||
|
<div class="field">
|
||||||
|
${field.render()|n}
|
||||||
|
</div>
|
||||||
|
% if 'instructions' in field.metadata:
|
||||||
|
<span class="instructions">${field.metadata['instructions']}</span>
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
% if (form.focus == field or form.focus is True) and not _focus_rendered:
|
||||||
|
% if not field.is_readonly():
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
$(function() {
|
||||||
|
$('#${field.renderer.name}').focus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<% _focus_rendered = True %>
|
||||||
|
% endif
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% endfor
|
||||||
|
|
||||||
|
% if form.successive:
|
||||||
|
<div class="checkbox">
|
||||||
|
${h.checkbox('keep-going', checked=True)}
|
||||||
|
<label for="keep-going">Add another after this one</label>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
% if buttons:
|
||||||
|
${capture(buttons)}
|
||||||
|
% else:
|
||||||
|
${h.submit('submit', "Save")}
|
||||||
|
<button type="button" class="cancel">Cancel</button>
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
${h.end_form()}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
$(function() {
|
||||||
|
$('button.cancel').click(function() {
|
||||||
|
location.href = '${form.home_url}';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -27,65 +27,13 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyramid.httpexceptions import HTTPFound
|
from pyramid.httpexceptions import HTTPFound
|
||||||
from pyramid.renderers import render_to_response
|
|
||||||
from pyramid.security import authenticated_userid
|
from pyramid.security import authenticated_userid
|
||||||
|
|
||||||
from webhelpers.html import literal
|
from webhelpers.html import literal
|
||||||
from webhelpers.html.tags import link_to
|
from webhelpers.html.tags import link_to
|
||||||
|
|
||||||
from edbob.pyramid import Session
|
from edbob.pyramid.views.autocomplete import *
|
||||||
from edbob.pyramid.forms.formalchemy import AutocompleteFieldRenderer
|
from edbob.pyramid.views.form import *
|
||||||
from edbob.util import requires_impl
|
|
||||||
|
|
||||||
|
|
||||||
class Autocomplete(object):
|
|
||||||
|
|
||||||
def __init__(self, request):
|
|
||||||
self.request = request
|
|
||||||
|
|
||||||
@property
|
|
||||||
@requires_impl(is_property=True)
|
|
||||||
def mapped_class(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@property
|
|
||||||
@requires_impl(is_property=True)
|
|
||||||
def fieldname(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@property
|
|
||||||
@requires_impl(is_property=True)
|
|
||||||
def route_name(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def __call__(self):
|
|
||||||
query = self.request.params['query']
|
|
||||||
q = Session.query(self.mapped_class)
|
|
||||||
q = q.filter(getattr(self.mapped_class, self.fieldname).ilike('%%%s%%' % query))
|
|
||||||
objs = q.order_by(getattr(self.mapped_class, self.fieldname)).all()
|
|
||||||
data = dict(
|
|
||||||
query=query,
|
|
||||||
suggestions=[getattr(x, self.fieldname) for x in objs],
|
|
||||||
data=[x.uuid for x in objs],
|
|
||||||
)
|
|
||||||
response = render_to_response('json', data, request=self.request)
|
|
||||||
response.headers['Content-Type'] = 'application/json'
|
|
||||||
return response
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def add_route(cls, config, url):
|
|
||||||
# def add_routes(cls, config, route_prefix, url_prefix, template_prefix=None, permission_prefix=None):
|
|
||||||
"""
|
|
||||||
Add 'autocomplete' route to the config object.
|
|
||||||
"""
|
|
||||||
|
|
||||||
config.add_route(cls.route_name, url)
|
|
||||||
config.add_view(cls, route_name=cls.route_name, http_cache=0)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def renderer(cls, request):
|
|
||||||
return AutocompleteFieldRenderer(request.route_url(cls.route_name),
|
|
||||||
(cls.mapped_class, cls.fieldname))
|
|
||||||
|
|
||||||
|
|
||||||
def forbidden(request):
|
def forbidden(request):
|
||||||
|
|
105
edbob/pyramid/views/autocomplete.py
Normal file
105
edbob/pyramid/views/autocomplete.py
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# edbob -- Pythonic Software Framework
|
||||||
|
# Copyright © 2010-2012 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of edbob.
|
||||||
|
#
|
||||||
|
# edbob is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# edbob is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with edbob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
``edbob.pyramid.views.autocomplete`` -- Autocomplete View
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pyramid.renderers import render_to_response
|
||||||
|
|
||||||
|
from edbob.pyramid import Session
|
||||||
|
from edbob.pyramid.forms.formalchemy import AutocompleteFieldRenderer
|
||||||
|
from edbob.util import requires_impl
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['Autocomplete']
|
||||||
|
|
||||||
|
|
||||||
|
class Autocomplete(object):
|
||||||
|
|
||||||
|
route = None
|
||||||
|
url = None
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
self.request = request
|
||||||
|
|
||||||
|
@property
|
||||||
|
@requires_impl(is_property=True)
|
||||||
|
def mapped_class(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
@requires_impl(is_property=True)
|
||||||
|
def fieldname(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_route(cls):
|
||||||
|
if not cls.route:
|
||||||
|
name = cls.mapped_class.__name__.lower()
|
||||||
|
cls.route = '%ss.autocomplete' % name
|
||||||
|
return cls.route
|
||||||
|
|
||||||
|
def filter_query(self, q):
|
||||||
|
return q
|
||||||
|
|
||||||
|
def make_query(self, query):
|
||||||
|
q = Session.query(self.mapped_class)
|
||||||
|
q = self.filter_query(q)
|
||||||
|
q = q.filter(getattr(self.mapped_class, self.fieldname).ilike('%%%s%%' % query))
|
||||||
|
q = q.order_by(getattr(self.mapped_class, self.fieldname))
|
||||||
|
return q
|
||||||
|
|
||||||
|
def query(self, query):
|
||||||
|
return self.make_query(query)
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
query = self.request.params['query']
|
||||||
|
objs = self.query(query).all()
|
||||||
|
data = dict(
|
||||||
|
query=query,
|
||||||
|
suggestions=[getattr(x, self.fieldname) for x in objs],
|
||||||
|
data=[x.uuid for x in objs],
|
||||||
|
)
|
||||||
|
response = render_to_response('json', data, request=self.request)
|
||||||
|
response.headers['Content-Type'] = 'application/json'
|
||||||
|
return response
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_route(cls, config):
|
||||||
|
"""
|
||||||
|
Add 'autocomplete' route to the config object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = cls.mapped_class.__name__.lower()
|
||||||
|
route = cls.get_route()
|
||||||
|
url = cls.url or '/%ss/autocomplete' % name
|
||||||
|
|
||||||
|
config.add_route(route, url)
|
||||||
|
config.add_view(cls, route_name=route, http_cache=0)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def renderer(cls, request):
|
||||||
|
return AutocompleteFieldRenderer(request.route_url(cls.get_route()),
|
||||||
|
(cls.mapped_class, cls.fieldname))
|
|
@ -37,6 +37,7 @@ from pyramid.httpexceptions import HTTPFound, HTTPException
|
||||||
# from rattail.db.perms import has_permission
|
# from rattail.db.perms import has_permission
|
||||||
# from rattail.pyramid.forms.formalchemy import Grid
|
# from rattail.pyramid.forms.formalchemy import Grid
|
||||||
|
|
||||||
|
import edbob
|
||||||
from edbob.db import Base
|
from edbob.db import Base
|
||||||
from edbob.pyramid import forms
|
from edbob.pyramid import forms
|
||||||
from edbob.pyramid import Session
|
from edbob.pyramid import Session
|
||||||
|
@ -45,28 +46,42 @@ from edbob.util import requires_impl
|
||||||
|
|
||||||
class Crud(object):
|
class Crud(object):
|
||||||
|
|
||||||
|
routes = ['new', 'edit', 'delete']
|
||||||
|
|
||||||
|
route_prefix = None
|
||||||
|
url_prefix = None
|
||||||
|
template_prefix = None
|
||||||
|
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
self.request = request
|
self.request = request
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@requires_impl(is_property=True)
|
@requires_impl(is_property=True)
|
||||||
def mapped_class(self):
|
def mapped_class(self):
|
||||||
raise NotImplementedError
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@requires_impl(is_property=True)
|
@requires_impl(is_property=True)
|
||||||
def list_route(self):
|
def home_route(self):
|
||||||
raise NotImplementedError
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def list_url(self):
|
def home_url(self):
|
||||||
return self.request.route_url(self.list_route)
|
return self.request.route_url(self.home_route)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cancel_route(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def permission_prefix(self):
|
||||||
|
return self.route_prefix + 's'
|
||||||
|
|
||||||
def make_fieldset(self, model, **kwargs):
|
def make_fieldset(self, model, **kwargs):
|
||||||
if 'action_url' not in kwargs:
|
if 'action_url' not in kwargs:
|
||||||
kwargs['action_url'] = self.request.current_route_url()
|
kwargs['action_url'] = self.request.current_route_url()
|
||||||
if 'list_url' not in kwargs:
|
if 'home_url' not in kwargs:
|
||||||
kwargs['list_url'] = self.list_url
|
kwargs['home_url'] = self.home_url
|
||||||
return forms.make_fieldset(model, **kwargs)
|
return forms.make_fieldset(model, **kwargs)
|
||||||
|
|
||||||
def fieldset(self, obj):
|
def fieldset(self, obj):
|
||||||
|
@ -80,25 +95,55 @@ class Crud(object):
|
||||||
fs = self.make_fieldset(obj)
|
fs = self.make_fieldset(obj)
|
||||||
return fs
|
return fs
|
||||||
|
|
||||||
def crud(self, obj):
|
def post_sync(self, fs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def crud(self, obj=None):
|
||||||
|
if obj is None:
|
||||||
|
obj = self.mapped_class
|
||||||
|
|
||||||
|
# fs = self.fieldset(obj)
|
||||||
|
# if not fs.readonly and self.request.POST:
|
||||||
|
# fs.rebind(data=self.request.params)
|
||||||
|
# if fs.validate():
|
||||||
|
|
||||||
|
# with transaction.manager:
|
||||||
|
# fs.sync()
|
||||||
|
# Session.add(fs.model)
|
||||||
|
# Session.flush()
|
||||||
|
# self.request.session.flash('%s "%s" has been %s.' % (
|
||||||
|
# fs.crud_title, fs.get_display_text(),
|
||||||
|
# 'updated' if fs.edit else 'created'))
|
||||||
|
|
||||||
|
# if self.request.params.get('add-another') == '1':
|
||||||
|
# return HTTPFound(location=self.request.current_route_url())
|
||||||
|
|
||||||
|
# return HTTPFound(location=self.home_url)
|
||||||
|
|
||||||
fs = self.fieldset(obj)
|
fs = self.fieldset(obj)
|
||||||
if not fs.readonly and self.request.POST:
|
if not fs.readonly and self.request.POST:
|
||||||
fs.rebind(data=self.request.params)
|
fs.rebind(data=self.request.params)
|
||||||
if fs.validate():
|
if fs.validate():
|
||||||
|
|
||||||
|
result = None
|
||||||
|
|
||||||
with transaction.manager:
|
with transaction.manager:
|
||||||
fs.sync()
|
fs.sync()
|
||||||
|
result = self.post_sync(fs)
|
||||||
|
if not result:
|
||||||
Session.add(fs.model)
|
Session.add(fs.model)
|
||||||
Session.flush()
|
Session.flush()
|
||||||
self.request.session.flash('%s "%s" has been %s.' % (
|
self.request.session.flash('%s "%s" has been %s.' % (
|
||||||
fs.crud_title, fs.get_display_text(),
|
fs.crud_title, fs.get_display_text(),
|
||||||
'updated' if fs.edit else 'created'))
|
'updated' if fs.edit else 'created'))
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return result
|
||||||
|
|
||||||
if self.request.params.get('add-another') == '1':
|
if self.request.params.get('add-another') == '1':
|
||||||
return HTTPFound(location=self.request.current_route_url())
|
return HTTPFound(location=self.request.current_route_url())
|
||||||
|
|
||||||
return HTTPFound(location=self.list_url)
|
return HTTPFound(location=self.home_url)
|
||||||
|
|
||||||
# TODO: This probably needs attention.
|
# TODO: This probably needs attention.
|
||||||
if not fs.edit:
|
if not fs.edit:
|
||||||
|
@ -121,31 +166,40 @@ class Crud(object):
|
||||||
assert obj
|
assert obj
|
||||||
with transaction.manager:
|
with transaction.manager:
|
||||||
Session.delete(obj)
|
Session.delete(obj)
|
||||||
return HTTPFound(location=self.request.route_url(self.list_route))
|
return HTTPFound(location=self.home_url)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_routes(cls, config, route_prefix, url_prefix, template_prefix=None, permission_prefix=None):
|
def add_routes(cls, config):
|
||||||
"""
|
"""
|
||||||
Add standard routes (i.e. 'new', 'edit' and 'delete') for the mapped
|
Add routes to the config object.
|
||||||
class to the config object.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not template_prefix:
|
routes = cls.routes
|
||||||
template_prefix = url_prefix
|
if isinstance(routes, list):
|
||||||
if not permission_prefix:
|
_routes = routes
|
||||||
permission_prefix = route_prefix + 's'
|
routes = {}
|
||||||
|
for route in _routes:
|
||||||
|
routes[route] = {}
|
||||||
|
|
||||||
config.add_route('%s.new' % route_prefix, '%s/new' % url_prefix)
|
route_prefix = cls.route_prefix or cls.mapped_class.__name__.lower()
|
||||||
config.add_view(cls, attr='new', route_name='%s.new' % route_prefix, renderer='%s/crud.mako' % template_prefix,
|
url_prefix = cls.url_prefix or '/%ss' % route_prefix
|
||||||
permission='%s.create' % permission_prefix, http_cache=0)
|
template_prefix = cls.template_prefix or url_prefix
|
||||||
|
permission_prefix = cls.permission_prefix or '%ss' % route_prefix
|
||||||
|
|
||||||
config.add_route('%s.edit' % route_prefix, '%s/{uuid}/edit' % url_prefix)
|
for action in routes:
|
||||||
config.add_view(cls, attr='edit', route_name='%s.edit' % route_prefix, renderer='%s/crud.mako' % template_prefix,
|
kw = dict(
|
||||||
permission='%s.edit' % permission_prefix, http_cache=0)
|
route='%s.%s' % (route_prefix, action),
|
||||||
|
renderer='%s/%s.mako' % (template_prefix, action),
|
||||||
config.add_route('%s.delete' % route_prefix, '%s/{uuid}/delete' % url_prefix)
|
permission='%s.%s' % (permission_prefix, dict(new='create').get(action, action)),
|
||||||
config.add_view(cls, attr='delete', route_name='%s.delete' % route_prefix,
|
)
|
||||||
permission='%s.delete' % permission_prefix, http_cache=0)
|
if action == 'new':
|
||||||
|
kw['url'] = '%s/new' % url_prefix
|
||||||
|
else:
|
||||||
|
kw['url'] = '%s/{uuid}/%s' % (url_prefix, action)
|
||||||
|
kw.update(routes[action])
|
||||||
|
config.add_route(kw['route'], kw['url'])
|
||||||
|
config.add_view(cls, attr=action, route_name=kw['route'], renderer=kw['renderer'],
|
||||||
|
permission=kw['permission'], http_cache=0)
|
||||||
|
|
||||||
|
|
||||||
def crud(request, cls, fieldset_factory, home=None, delete=None, post_sync=None, pre_render=None):
|
def crud(request, cls, fieldset_factory, home=None, delete=None, post_sync=None, pre_render=None):
|
||||||
|
|
98
edbob/pyramid/views/form.py
Normal file
98
edbob/pyramid/views/form.py
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# edbob -- Pythonic Software Framework
|
||||||
|
# Copyright © 2010-2012 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of edbob.
|
||||||
|
#
|
||||||
|
# edbob is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# edbob is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with edbob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
``edbob.pyramid.views.form`` -- Generic Form View
|
||||||
|
"""
|
||||||
|
|
||||||
|
import transaction
|
||||||
|
from pyramid.httpexceptions import HTTPFound
|
||||||
|
|
||||||
|
from edbob.pyramid import Session
|
||||||
|
from edbob.pyramid.forms import SimpleForm
|
||||||
|
from edbob.util import requires_impl
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['FormView']
|
||||||
|
|
||||||
|
|
||||||
|
class FormView(object):
|
||||||
|
"""
|
||||||
|
This view provides basic form processing goodies.
|
||||||
|
"""
|
||||||
|
|
||||||
|
route = None
|
||||||
|
url = None
|
||||||
|
template = None
|
||||||
|
permission = None
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
self.request = request
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
"""
|
||||||
|
Callable for the view. This method creates the underlying form and
|
||||||
|
processes data if any was submitted.
|
||||||
|
"""
|
||||||
|
|
||||||
|
f = self.form(self.request)
|
||||||
|
if not f.readonly and self.request.POST:
|
||||||
|
f.rebind(data=self.request.params)
|
||||||
|
if f.validate():
|
||||||
|
|
||||||
|
with transaction.manager:
|
||||||
|
f.save(Session)
|
||||||
|
Session.flush()
|
||||||
|
self.request.session.flash('The book "%s" has been loaned.' % f.book.value)
|
||||||
|
|
||||||
|
if self.request.params.get('keep-going') == '1':
|
||||||
|
return HTTPFound(location=self.request.current_route_url())
|
||||||
|
|
||||||
|
return HTTPFound(location=f.home_url)
|
||||||
|
|
||||||
|
return {'form': f}
|
||||||
|
|
||||||
|
def make_form(self, request, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns a :class:`edbob.pyramid.forms.Form` instance.
|
||||||
|
"""
|
||||||
|
template = kwargs.pop('template', self.template or '%s.mako' % self.url)
|
||||||
|
return SimpleForm(request, template=template, **kwargs)
|
||||||
|
|
||||||
|
def form(self, request):
|
||||||
|
"""
|
||||||
|
Should create and return a :class:`edbob.pyramid.forms.Form` instance
|
||||||
|
for the view.
|
||||||
|
"""
|
||||||
|
return self.make_form(request)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_route(cls, config, **kwargs):
|
||||||
|
route = kwargs.get('route', cls.route)
|
||||||
|
url = kwargs.get('url', cls.url)
|
||||||
|
permission = kwargs.get('permission', cls.permission or route)
|
||||||
|
template = kwargs.get('template', cls.template or '%s.mako' % url)
|
||||||
|
config.add_route(route, url)
|
||||||
|
config.add_view(cls, route_name=route, renderer=template,
|
||||||
|
permission=permission, http_cache=0)
|
Loading…
Add table
Add a link
Reference in a new issue