Skip to content

Instantly share code, notes, and snippets.

@Ignas
Created November 16, 2012 14:01
Show Gist options
  • Save Ignas/4087573 to your computer and use it in GitHub Desktop.
Save Ignas/4087573 to your computer and use it in GitHub Desktop.
Simple formencode based form library for mako templates
import formencode
from formencode import htmlfill, variabledecode
from webhelpers.html.builder import literal
from zope.cachedescriptors.property import Lazy
class FormEncodeState(object):
def __init__(self, request):
self.request = request
def error_formatter(message):
return '<div class="error-message">%s</div>' % message
class FormBase(object):
def __init__(self, context, request):
self.request = request
self.context = context
self.errors = {}
def validate_schema(self, schema, variable_decode=True,
dict_char='.', list_char='-'):
if variable_decode:
decoded = variabledecode.variable_decode(self.request.params, dict_char, list_char)
errors = {}
form_result = {}
try:
form_result = schema.to_python(decoded,
FormEncodeState(self.request))
except formencode.Invalid, e:
errors = e.unpack_errors(variable_decode, dict_char, list_char)
return form_result, errors
def work(self):
if self.action in self.request.params:
values, errors = self.validate
if not errors:
return self.apply(self.context, values)
else:
self.errors = errors
def defaults(self):
return {}
def defaults_from_request(self, request):
return request.params
# XXX solving double validation using Lazy
@Lazy
def validate(self):
return self.validate_schema(self.schema)
def __call__(self, form_content):
# Defaults can be given either in a dict or a callable
defaults = self.defaults() if hasattr(self.defaults, '__call__') else self.defaults
errors = {}
if self.action in self.request.params:
_, errors = self.validate
defaults = self.defaults_from_request(self.request)
return literal(htmlfill.render(form_content,
defaults=defaults,
auto_error_formatter=error_formatter,
errors=errors))
class Form(FormBase):
"""Class for one form views.
To define a form do something like in __init__ of the view:
>> self.form = Form(context, request,
.. apply=self.apply,
.. defaults=self.defaults,
.. schema=FormSchema(),
.. action='UPDATE')
"""
def apply(self, context, values):
return values
def __init__(self, context, request, schema, action, apply=None, defaults=None):
self.request = request
self.context = context
self.schema = schema
if apply is not None:
self.apply = apply
self.action = action
if defaults is not None:
self.defaults = defaults
"""
Add this to your base.mako
<%def name="form(filler)">
${filler(capture(caller.body))}
</%def>
if you pass your form as add_form into in template context you can do
this:
<%self:form filler="${add_form}">
<form method="post">
<input type="text" name="title" />
<input type="submit" name="ADD" value="Add" />
</form>
</%self:form>
"""
@Ignas
Copy link
Author

Ignas commented Nov 16, 2012

def my_view(request):
location = request.context
form = Form(location, request,
schema=SubDepartmentAddForm(),
action='ADD')

    result = form.work()
    if result is not None:
        sub_department = SubDepartment(result['title'], location)
        sub_department.description = result['description']
        meta.Session.commit()
        redirect(location.url(action='sub_departments'))
return {'add_form': form}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment