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 javascript
#-*- coding:utf-8 -*- from formalchemy import templates from 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中定義字段的label def field_name(field): return field.label_text or field.name class 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 css
#-*- coding:utf-8 -*- from formalchemy import config, validators, Field, FieldSet from customEngine import Jinja2Engine from 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代碼。
{%- if fieldset.errors %} <div class="alert alert-error form-error"> {% for field in fieldset.errors %} <p> <label for="{{ field|field_label }}"> <strong>{{ field|field_name }}:</strong> </label> <p> {% for e in field.errors %} <p>{{ e }}</p> {% endfor %} </p> </p> {% endfor %} </div> {% endif -%} {% for field in fieldset.render_fields.itervalues() %} {% if field.requires_label %} <div class="control-group"> <label class="control-label" for="{{ field|field_label }}"> {% if field.is_required() %} <strong style="color: red;">*</strong> {% endif %} <strong>{{ field|field_name }}:</strong> </label> <div class="controls"> {% 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"] %} <p class="help-block"> {{ i }} </p> {% endfor %} {% else %} <p class="help-block"> {{ field.metadata["instructions"] }} </p> {% endif %} {% endif %} </div> </div> {% else %} {{ field.render() }} {% endif %} {% endfor %}main.py
#-*- coding:utf-8 -*- import web, middleware from web.contrib.template import render_jinja from 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.orm class 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 html
form.html
前端
<!DOCTYPE html> <html> <head> <title></title> <link type="text/css" href="/static/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="/static/js/jquery.min.js"></script> <script type="text/javascript" src="/static/js/bootstrap.min.js"></script> </head> <body> <div class="container"> <div class="row"> <div class="span6 offset3"> <form class="form-horizontal validate" action="" method="post"> {{ form.render() }} <div style="text-align: center;"> <input type="submit" class="btn btn-large" value="Submit" /> </div> </form> </div> </div> </div> </body> </html>
運行main.py,而後訪問/form/頁面,看看是否是出來了一個整齊的表單:) java