formalchemy是一个强大的表单表格生成框架,几乎所有的动态web开发里都需要处理form表单,而form表单要处理的几个方面包括:表单展现,表单服务端验证,表单前端验证(既js验证),表单数据插入数据库。formalchemy在这几方面都做的非常好,特别是对掌控欲特别强又很懒的人来说:)。由于formalchemy是跟sqlalchemy是配套的(两者名字是如此相似),所以我们可以很好的与前面所创建的User表关联起来。
这一章我们要解决的是关于form的展现,formalchemy自身支持三种template engine,分别是:moko,genshi,tempita,除了mako我没用过,另外两个我都用过了,老实说,不怎么样,但是formalchemy为我们这种有需求的人提供了解决方案,customize templates,这里我要介绍的就是用jinja2定制一个让我们随便玩的template engine,上代码。 customEngine.py#-*- coding:utf-8 -*-from formalchemy import templatesfrom jinja2 import Environment,FileSystemLoader#定义一个方法用来获取formalchemy输出input的name属性def field_label(field): return field.parent._format % dict([('model',field.model.__class__.__name__), ('pk',field.parent.model.id or ''), ('name',field.name)])#定义一个方法用来获取formalchemy中定义字段的labeldef field_name(field): return field.label_text or field.nameclass Jinja2Engine(templates.TemplateEngine): extension = 'jinja2' def get_template(self, name, **kw): self._lookup = Environment(loader=FileSystemLoader(self.directories, **kw)) self._lookup.filters.update( #如果你想要在模板里添加filter,可以在这里添加 field_label = field_label, field_name = field_name, ) #增加这个扩展可以让我们在模板的循环块里使用break,continue self._lookup.add_extension('jinja2.ext.loopcontrols') #增加这个扩展可以让我们在模板里使用do关键字,用来进行一些python语句操作 self._lookup.add_extension('jinja2.ext.do') return self._lookup.get_template("%s.%s" % (name, self.extension)) def render(self, template_name, **kwargs): template = self._lookup.get_template("%s.%s" % (template_name, self.extension)) return template.render(**kwargs)
以上代码是通过重载formalchemy的TemplateEngine来实现一个定制的Engine,下面是如何调用这个Engine,代码中结合上一章的User表为例。
forms.py#-*- coding:utf-8 -*-from formalchemy import config, validators, Field, FieldSetfrom customEngine import Jinja2Enginefrom models import *class UserForm: def __init__(self): #这里的directories是指表单模板存放的地方,我们在第二章提到的templates下创建一个文件夹,命名为form config.engine = Jinja2Engine(directories=["templates/form"]) #为表单设置label def setLabel(self): self.name = self.fs.name.label("User Name") self.email = self.fs.email.label("Email Address") self.password = self.fs.password.label("Password") self.superuser = self.fs.superuser.label("Admin?") #定义编辑模式下通用的设置,编辑模式包括:新增,修改 def wmode(self, password=None): self.setLabel() #因为新增和修改中都需要用户重新确认密码,所以要为表单加入Confirm Password #如果有指定password的值,说明用户是在修改记录,那么Confirm Password也必须有值 if not password: self.fs.insert_after(self.fs.password, Field("confirm_password")) else: self.fs.insert_after(self.fs.password, Field("confirm_password", value=password)) self.confirm_password = self.fs.confirm_password.label("Re-enter Password") self.name = self.name.required() self.email = self.email.required().email().validate(validators.email) self.password = self.password.required().password() self.confirm_password = self.confirm_password.required().password() #定义新增用户时调用的方法 def write_render(self, cls): #设置Fieldset对象,指定要绑定的sqlalchemy中的表类,并赋予sqlalchemy的session self.fs = FieldSet(User, session=cls.db) self.wmode() #配置表单信息 self.fs.configure( #表单包含的字段 include=[ self.name, self.email, self.password, self.confirm_password ] ) return self.fsok,接下来我们要制作一个模板用来展现表单,只要没啥特殊改变,整个项目都可以使用这个模板,这里我们还是使用bootstrap来渲染页面,首先我们在templates目录下创建form目录,form目录中创建一个fieldset.jinja2,fieldset_readonly.jinja2,grid.jinja2,grid_readonly.jinja2,fieldset.jinja2为表单模板,这一章我们只讲fieldset.jinja2,下面是fieldset.jinja2代码。 fieldset.jinja2
{%- if fieldset.errors %}main.py{% for field in fieldset.errors %}{% endif -%}{% for field in fieldset.render_fields.itervalues() %} {% if field.requires_label %}
{% for e in field.errors %}
{
{ e }} {% endfor %} {% endfor %}{% else %} { { field.render() }} {% endif %}{% endfor %}{% if field.is_required() %} { { field.with_html(required_='required').render() }} {% else %} { { field.render() }} {% endif %} {% if 'instructions' in field.metadata %} {% if field.metadata["instructions"] is iterable %} {% for i in field.metadata["instructions"] %}{
{ i }} {% endfor %} {% else %}{
{ field.metadata["instructions"] }} {% endif %} {% endif %}
#-*- coding:utf-8 -*-import web, middlewarefrom web.contrib.template import render_jinjafrom models import *from forms import *urls = ( "/", "index", "/form/", "showform",)app = web.application(urls, globals())app.add_processor(middleware.set_orm)render = render_jinja( 'templates', encoding = 'utf-8',)class BaseView(object): def __init__(self): #从web.ctx.orm获取session放入基类的db中 self.db = web.ctx.ormclass index(BaseView): def GET(self): return render.index()class showform(BaseView): def GET(self): form = UserForm() fs = form.write_render(self) return render.form(form=fs)if __name__ == "__main__": app.run()
最后在templates目录中添加form.html
form.html
运行main.py,然后访问/form/页面,看看是不是出来了一个整齐的表单:)