convenience commit (form refactoring)

This commit is contained in:
Lance Edgar 2012-07-11 00:49:43 -05:00
parent dd3b42d981
commit f0c8858fb0
19 changed files with 736 additions and 184 deletions

View file

@ -44,7 +44,7 @@ inited = False
engines = None
engine = None
Session = sessionmaker()
Base = declarative_base()
Base = declarative_base(cls=edbob.Object)
# metadata = None

View file

@ -1,29 +1,31 @@
#!/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`` -- Forms
"""
from edbob.pyramid.forms.formalchemy import *
#!/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`` -- Forms
"""
from edbob.pyramid.forms.core import *
from edbob.pyramid.forms.formalchemy import *
from edbob.pyramid.forms.simpleform import *

View 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 ''

View file

@ -44,12 +44,14 @@ from formalchemy.validators import accepts_none
import edbob
from edbob.lib import pretty
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',
'EnumFieldRenderer', 'PrettyDateTimeFieldRenderer',
'AutocompleteFieldRenderer',
'AutocompleteFieldRenderer', 'FieldSet',
'make_fieldset', 'required', 'pretty_datetime']
@ -68,33 +70,6 @@ engine = TemplateEngine()
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):
"""
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)
def make_fieldset(model, **kwargs):
kwargs.setdefault('session', Session())
return FieldSet(model, **kwargs)
@accepts_none
def required(value, field=None):
if value is None or value == '':
@ -371,6 +341,23 @@ class PrettyDateTimeFieldRenderer(formalchemy.fields.DateTimeFieldRenderer):
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):
"""
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('-', '_')
return formalchemy.config.engine('field_autocomplete', fieldname=self.name,
fieldvalue=self.value, display=self._display(self.value),
autocompleter_name=autocompleter_name,
autocompleter=autocompleter_name,
service_url=self.service_url, width=self.width,
callback=self.callback, hidden=tags.hidden, text=tags.text,
**kwargs)
callback=self.callback, h=helpers, **kwargs)

View 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)

View 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)

View file

@ -1,7 +1,8 @@
<%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="home_link()"><h1 class="right">${h.link_to("Home", url('home'))}</h1></%def>
<%def name="menu()"></%def>
<%def name="footer()">
powered by ${h.link_to('edbob', 'http://edbob.org', target='_blank')} v${edbob.__version__}
</%def>

View file

@ -1,5 +1,7 @@
<%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="right">

View 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>

View 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>

View file

@ -40,21 +40,21 @@ ${h.image(logo_url, "${self.global_title()} logo", id='login-logo', **logo_kwarg
$(function() {
$('form').submit(function() {
if (! $('#username').val()) {
with ($('#username').get(0)) {
select();
focus();
}
return false;
}
if (! $('#password').val()) {
with ($('#password').get(0)) {
select();
focus();
}
return false;
}
return true;
if (! $('#username').val()) {
with ($('#username').get(0)) {
select();
focus();
}
return false;
}
if (! $('#password').val()) {
with ($('#password').get(0)) {
select();
focus();
}
return false;
}
return true;
});
$('#username').focus();

View file

@ -0,0 +1,2 @@
<%inherit file="/edbob/form.mako" />
${parent.body()}

View file

@ -1,6 +1,6 @@
<div id="${fieldname}-container" class="autocomplete-container">
${hidden(fieldname, id=fieldname, value=fieldvalue)}
${text(fieldname+'-textbox', id=fieldname+'-textbox', value=display,
${h.hidden(fieldname, id=fieldname, value=fieldvalue)}
${h.text(fieldname+'-textbox', id=fieldname+'-textbox', value=display,
class_='autocomplete-textbox', style='display: none;' if fieldvalue else '')}
<div id="${fieldname}-display" class="autocomplete-display"${'' if fieldvalue else ' style="display: none;"'|n}>
<span>${display}</span>
@ -9,18 +9,18 @@
</div>
<script language="javascript" type="text/javascript">
$(function() {
var ${autocompleter_name} = $('#${fieldname}-textbox').autocomplete({
serviceUrl: '${service_url}',
width: '${width}',
onSelect: function(value, data) {
$('#${fieldname}').val(data);
$('#${fieldname}-display span').text(value);
$('#${fieldname}-textbox').hide();
$('#${fieldname}-display').show();
% if callback:
${callback}(value, data);
% endif
},
var ${autocompleter} = $('#${fieldname}-textbox').autocomplete({
serviceUrl: '${service_url}',
width: '${width}',
onSelect: function(value, data) {
$('#${fieldname}').val(data);
$('#${fieldname}-display span').text(value);
$('#${fieldname}-textbox').hide();
$('#${fieldname}-display').show();
% if callback:
${callback}(value, data);
% endif
},
});
});
</script>

View file

@ -52,7 +52,7 @@
<script language="javascript" type="text/javascript">
$(function() {
$('button.cancel').click(function() {
location.href = '${fieldset.cancel_url}';
location.href = '${fieldset.home_url}';
});
});
</script>

View 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>

View file

@ -27,67 +27,15 @@
"""
from pyramid.httpexceptions import HTTPFound
from pyramid.renderers import render_to_response
from pyramid.security import authenticated_userid
from webhelpers.html import literal
from webhelpers.html.tags import link_to
from edbob.pyramid import Session
from edbob.pyramid.forms.formalchemy import AutocompleteFieldRenderer
from edbob.util import requires_impl
from edbob.pyramid.views.autocomplete import *
from edbob.pyramid.views.form import *
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):
"""
The forbidden view. This is triggered whenever access rights are denied

View 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))

View file

@ -37,6 +37,7 @@ from pyramid.httpexceptions import HTTPFound, HTTPException
# from rattail.db.perms import has_permission
# from rattail.pyramid.forms.formalchemy import Grid
import edbob
from edbob.db import Base
from edbob.pyramid import forms
from edbob.pyramid import Session
@ -45,28 +46,42 @@ from edbob.util import requires_impl
class Crud(object):
routes = ['new', 'edit', 'delete']
route_prefix = None
url_prefix = None
template_prefix = None
def __init__(self, request):
self.request = request
@property
@requires_impl(is_property=True)
def mapped_class(self):
raise NotImplementedError
pass
@property
@requires_impl(is_property=True)
def list_route(self):
raise NotImplementedError
def home_route(self):
pass
@property
def list_url(self):
return self.request.route_url(self.list_route)
def home_url(self):
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):
if 'action_url' not in kwargs:
kwargs['action_url'] = self.request.current_route_url()
if 'list_url' not in kwargs:
kwargs['list_url'] = self.list_url
if 'home_url' not in kwargs:
kwargs['home_url'] = self.home_url
return forms.make_fieldset(model, **kwargs)
def fieldset(self, obj):
@ -80,25 +95,55 @@ class Crud(object):
fs = self.make_fieldset(obj)
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)
if not fs.readonly and self.request.POST:
fs.rebind(data=self.request.params)
if fs.validate():
result = None
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'))
result = self.post_sync(fs)
if not result:
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 result:
return result
if self.request.params.get('add-another') == '1':
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.
if not fs.edit:
@ -121,31 +166,40 @@ class Crud(object):
assert obj
with transaction.manager:
Session.delete(obj)
return HTTPFound(location=self.request.route_url(self.list_route))
return HTTPFound(location=self.home_url)
@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
class to the config object.
Add routes to the config object.
"""
if not template_prefix:
template_prefix = url_prefix
if not permission_prefix:
permission_prefix = route_prefix + 's'
routes = cls.routes
if isinstance(routes, list):
_routes = routes
routes = {}
for route in _routes:
routes[route] = {}
config.add_route('%s.new' % route_prefix, '%s/new' % url_prefix)
config.add_view(cls, attr='new', route_name='%s.new' % route_prefix, renderer='%s/crud.mako' % template_prefix,
permission='%s.create' % permission_prefix, http_cache=0)
route_prefix = cls.route_prefix or cls.mapped_class.__name__.lower()
url_prefix = cls.url_prefix or '/%ss' % route_prefix
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)
config.add_view(cls, attr='edit', route_name='%s.edit' % route_prefix, renderer='%s/crud.mako' % template_prefix,
permission='%s.edit' % permission_prefix, http_cache=0)
config.add_route('%s.delete' % route_prefix, '%s/{uuid}/delete' % url_prefix)
config.add_view(cls, attr='delete', route_name='%s.delete' % route_prefix,
permission='%s.delete' % permission_prefix, http_cache=0)
for action in routes:
kw = dict(
route='%s.%s' % (route_prefix, action),
renderer='%s/%s.mako' % (template_prefix, action),
permission='%s.%s' % (permission_prefix, dict(new='create').get(action, action)),
)
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):

View 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)