web.py开发web 第七章 Formalchemy + Jinja2

有些话、适合烂在心里 提交于 2019-11-30 08:47:48

    formalchemy是一个强大的表单表格生成框架,几乎所有的动态web开发里都需要处理form表单,而form表单要处理的几个方面包括:表单展现,表单服务端验证,表单前端验证(既js验证),表单数据插入数据库。formalchemy在这几方面都做的非常好,特别是对掌控欲特别强又很懒的人来说:)。由于formalchemy是跟sqlalchemy是配套的(两者名字是如此相似),所以我们可以很好的与前面所创建的User表关联起来。
    这一章我们要解决的是关于form的展现,formalchemy自身支持三种template engine,分别是:mokogenshitempita,除了mako我没用过,另外两个我都用过了,老实说,不怎么样,但是formalchemy为我们这种有需求的人提供了解决方案,customize templates,这里我要介绍的就是用jinja2定制一个让我们随便玩的template engine,上代码。
customEngine.py

#-*- 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

#-*- 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.fs
    ok,接下来我们要制作一个模板用来展现表单,只要没啥特殊改变,整个项目都可以使用这个模板,这里我们还是使用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 %}
    <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

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/页面,看看是不是出来了一个整齐的表单:)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!