#coding:utf-8
from mypy.route_render import route_render_func
@route_render_func
def apply():
pass
用户注册是大部分网站最基本的功能。
这里,用户注册分两步,四个页面。
第一步,
1.填写邮箱,提交注册申请
2.发出邮件,提示用户接收
第二步,
1.用户激活,填写详细信息
2.激活成功,跳转欢迎页面
每一步又对应一个URL,设计URL也有很多讲究。用一句话概述就是:简短好记,避免更变(有兴趣可以读读 Cool URI don't change http://www.w3.org/Provider/Style/URI )。
这里,我们使用 /auth/apply 和 /auth/active 。
还是先动手写页面模板,然后慢慢来讲解。
在 mysite/ctrl 目录下 新建 auth.py , 内容如下
#coding:utf-8
from mypy.route_render import route_render_func
@route_render_func
def apply():
pass
同时在 init_url.py 中添加一行
import auth
在 myfile/css my 文件夹中新建 bform.css , 内容如下
.FormTip{text-align:center;margin-top:40px;padding-bottom:35px}
.Pform{margin:0 auto;width:335px;}
.Bform select{font-size:16px;margin-left:-6px;}
.Bform .Err,.Bform span{display:block;margin-top:5px;font-size:14px;color:#666;text-align:left;margin-left:38px}
.Bform p label{margin-right:10px}
.Bform p input{width:286px}
.Bform p{margin-bottom:20px;font-size:14px}
.Bform .Err,.Err{padding-bottom:2px;font-size:12px;margin-top:0;color:#d30}
.Err{display:block}
.Warn{color:#d30;}
.Ok{color:#090;}
.HideErr .Err{display:none}
.Bbtn{font-size:16px;padding:3px 6px;_padding:0 6px;overflow:visible}
在 myfile/js 文件夹中新建 auth_apply.js ,内容如下
$(function(){
$(".Pform input:first").focus()
})
运行 myfile 下的 merge.py 。
在 mysite/htm 下新建目录 auth,然后新建模板 apply.htm 内容如下
<%inherit file="/htmbase/simple_base.htm" />
<%!
from myfile import js,css
%>
<%def name="html_head()" filter="trim">
<link href="${css.bform}" rel="stylesheet" type="text/css" />
<script src="${js.auth_apply}"></script>
</%def>
<%def name="html_body()" filter="trim">
<form method="POST" class="HideErr Bform Pform">
<p align="center" style="padding-bottom:10px">许多故事,从这里开始...</p>
<p>
<span class="Err" id="err_email"></span>
<label>邮箱</label><input autocomplete="off" type="text" value="" class="text" name="email" id="email"/>
<span>输入Email邮箱,接收确认邮件以开始注册。 </span>
</p>
<p>
<label>密码</label><input type="password" value="" class="text" name="password" id="password">
<span>最少6个字符,区分大小写。 </span>
</p>
<p>
<label>名号</label><input autocomplete="off" type="text" value="" class="text" name="name" id="name">
<span>中英文皆可,不要超过8个汉字。 </span>
</p>
<p align="center"><button class="Bbtn" type="submit">注册</button></p>
<p align="right" style="font-size:12px;color:#333">忘记密码了?
<a href="/auth/reset_password">点此找回</a>
</p>
</form>
</%def>
启动 dev_server.py 服务器,访问 http://localhost:9889/auth/apply ,页面如图。右上角的Logo图片是用 Logo Creatr( http://creatr.cc/creatr/ )生成的。
先来,一一讲解他们的用途。
mypy整个设计遵循了MVC( 模型-视图-控制器 )的理念。
MVC,是一种设计理念。它将数据的模型的表示,操作的响应,用户的界面分离开,使代码的逻辑更为清晰。
auth.py,对应就是MVC中C -- control(控制器) 。
这里只是简单的声明新函数,尚未编写响应代码。
这里 import 的 route_render_func
from mypy.route_render import route_render_func
route_render_func 是由 route 函数 与 render_func函数混合而成的函数。
route的作用是url route(url路由,有时也成为 url mapping,url映射),简单的说,就是将url对应到函数。
在Python世界中有很多这样模块,比如
Routes http://routes.groovie.org
werkzeug.routing http://werkzeug.pocoo.org/documentation/dev/routing.html
上面两个模块原理是基于正则,从url匹配到函数;但mypy采用的是从Quixote延伸出来的另外一种方式。
在mypy中,通过route,将python的模块-函数的层次结构与url进行了自动映射。
基本的规则是 模块 -> 函数名 -> 函数参数,比如这里 /auth/apply 就与 auth模块中apply函数 建立了映射。
以下划线开头的函数不会被映射。
用到了route的模块,需要在程序启动时加载到内存,于是,我们需要在 init_url.py 中导入 auth 模块,而 init_url.py 在 __init__.py 中被导入 。
css 和 js 很普通,无需多言。
apply.htm 是一个Moko模板文件 -- 不是原始Mako,而是为网页做了优化的hmako。hmako会删除网页中的多余空白。
在讲Mako的语法之前,还是先谈谈Python的页面模板。
所谓页面模板,就是将程序中的变量拼接成HTML字符串。
如同,《封神演义》中“一气化三清”的故事,Python的模板可以大致可以分为三大流派。
比较激进的左派,它们的观点是模板归模板,Python归Python。模板要让那些不懂编程前端工程师们也能修修改改,所以模板中不要出现Python代码。
其代表作品有:Django的模板,Jinja( http://jinja.pocoo.org/ ) 。
比较保守的右派,它们的代码很像php,是Python和HTML的混合体,这种模板的性能往往很出众。
其代表作品有:Quixote的PTL,pyTenjin ( http://www.kuwata-lab.com/tenjin/pytenjin-users-guide.html ) 。
第三派,自然是骑墙派了。骑墙派代表人物有
基于XML的模板Genshi ( http://genshi.edgewall.org/ ) ,其速度比较慢。
新兴的Mako( http://www.makotemplates.org/ ),Mako会自动将HTML编译成Python模块,所以性能出众。
鉴于Mako的性能,和优雅的语法,以及可以直接写Python代码的便利,mypy选用它来作为页面模板。
(Mako语法暂时不写了,以后来补吧,先给两个链接)
http://www.cnblogs.com/RChen/archive/2007/06/15/mako_doc_translation_2.html
http://www.cnblogs.com/RChen/archive/2007/06/15/mako_doc_translation_3.html
不过原来的call方法被废弃了,需要注意一下。
<%namespacename:defname>
As of Mako 0.2.3, any user-defined "tag" can be created against a namespace by using a tag with a name of the form namespacename:defname. The closed and open formats of such a tag are equivalent to an inline expression and the <%call> tag, respectively.
<%mynamespace:somedef param="some value">
this is the body
</%mynamespace:somedef>
To create custom tags which accept a body, see Calling a def with embedded content and/or other defs.
另外,这里的js引用方式比较奇特。
<%!
from myfile import js,css
%>
<%def name="html_head()" filter="trim">
<script src="${js.reg}"></script>
</%def>
到 myfile目录下的 js 和 css 目录下可以看到 __init__.py 文件,文件中有reg这些变量和对应js文件。
而此处,__init__.py 文件又是通过 merge.py 生成的。
这样做的好处,一来是可以方便的更换js和css的域名。
一般而言,大部分浏览器对同一个域名,在同一时刻只启用2个线程去加载。启用多个域名让浏览器使用更多的加载线程,从而加快页面的加载速度。
对静态文件用单独的域名,还可以减少在请求静态文件时,不必要的cookie数据发送,从而节约带宽。
另外,为了节约带宽,常常把静态文件设置浏览器永久缓存。
但这样会造成静态文件有更新时,客户不能同步更新后的文件。
通常的解决方案是,当js或css文件有更新时,同时改变网页中引用的文件名,强制浏览器刷新这些文件。
而 merge.py 在生成 __init__.py 时,会监测文件改动,然后智能地对有改动的文件生成新的引用名称。
部署线上服务器的时候,配合Url Rewrite,可以方便实现js和css文件的缓存刷新的自动化。