前面几篇教程简单讲述了如何使用 Pylons 进行 WEB 方面开发的步骤,包括简单的 Hello、表单和数据库操作等内容。本篇将描述一下如何在正式环境中发布基于 Pylons 的 WEB 应用。
测试环境:Nginx 0.8.53 + FastCGI 模式 (需要安装 flup 模块)
pip install flup # easy_install -U flup
测试代码,延用前面的 Hello 示例。
mac:python smallfish$ paster create -t pylons hello mac:python smallfish$ cd hello/ mac:hello smallfish$ paster controller hi mac:hello smallfish$ paster serve --reload development.ini
确保以上过程无错,访问:http://127.0.0.1:5000 可以看到默认页面。
好把,开始配置发布环境。
需要修改 development.ini 配置文件,找到 [server:main] 节点,修改其中的 use 方式(默认是 egg:Paste#http)。
[server:main] use = egg:PasteScript#flup_fcgi_thread host = 0.0.0.0 port = 5000
另外建议修改 [DEFAULT] 节点中 debug = false,以免错误会打印具体的环境和堆栈信息。
到这里 Pylons 部分以及设置好了,如果出现一下错误:
LookupError: Entry point 'flup_fcgi_thread' not found in egg 'Paste'
请注意上面的 use 部分里是 PasteScript 而不是 Paste哦。淡定淡定,别光看后面的 http 改成 flup_fcgi_thread。
下面修改 Nginx 的配置 nginx.conf,修改其中 / 部分设置。
location / { fastcgi_pass 127.0.0.1:5000; fastcgi_index index; fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; include fastcgi_params; fastcgi_intercept_errors off; }
其中 fastcgi_pass 中 host 和 port 对应 development.ini 中的配置。
好把,这里可以启动,看看是否正确,请注意相应的目录。
启动 Pylons 部分(启动 daemon 进程,或参考搜狐邮箱使用的 zdaemon 来管理进程):
mac:hello smallfish$ paster serve --reload development.ini --daemon start # 注意,正式环境中请去除 reload 参数 # daemon 后面参数有 start | stop | restart | status,可以很方便的管理进程
启动 Nginx 部分:
mac:~ smallfish$ sudo /opt/nginx/sbin/nginx
尝试一下访问: http://localhost/ 和 http://localhost/hi/index 。是不是都可以了?OK,到这里已经整合好了。
这里是采用 FastCGI 方式发布,当然还可以用默认 http 或者本地 socket 方式,然后通过 proxy_pass 方式转发过去也是可以的。
当然,这个只是简单示例,可能正式发布时候需要注意的事项比这个复杂的多。这里只是介绍一种方式而已。
希望通过这一系列的教程,希望能对喜欢 Python 和关注 Pylons 框架的朋友能有所帮助。
前几篇链接分别如下:
1. Pylons 入门实例教程 – Hello
2. Pylons 入门实例教程 – 表单和文件上传
3. Pylons 入门实例教程 – 数据库操作
4. Pylons 入门实例教程 – cookie 和 session
本篇讲述在 Pylons 里使用 cookie 和 session。
示例还是在上篇《Pylons 入门实例教程 – 数据库操作》的代码里继续添加。先来尝试下 cookie,添加新的 cookietest controller。
修改 index 方法,添加显示部分:
def index(self): name = 'NULL' if request.cookies.has_key('name'): name = request.cookies['name'] return 'cookie name=%s' % name
cookie 读取可以通过 request.cookies 对象,类似一个字典结构。需要注意的是读取时候用最好 has_key 判断下,这样避免抛 KeyError 异常。当然你也可以 try…catch 捕获一下。
再重新写一个方法,用来写 cookie。
def writecookie(self): response.set_cookie("name", "smallfish") return "write cookie ok"
这里只是简单设置一个值得,set_cookie 还有其他参数,具体如下:
set_cookie(self, key, value='', max_age=None, path='/', domain=None, secure=None, httponly=False, version=None, comment=None, expires=None, overwrite=False)
基本一般需要设置:max_age,path,domain,expires 这几个参数。
下面再来尝试一下 session:
smallfish@debian:~/workspace/python/hellodb$ paster controller sessiontest Creating /home/smallfish/workspace/python/hellodb/hellodb/controllers/sessiontest.py Creating /home/smallfish/workspace/python/hellodb/hellodb/tests/functional/test_sessiontest.py
和上面 cookie 例子类似,在 controller 里有两个方法,index 负责显示 session,writesession 负责写。
def index(self): name = session.get('name', 'NULL') return 'session name=%s' % name def writesession(self): session['name'] = 'smallfish' session.save() return "save session ok"
index 方法里 get 后面的 NULL 是默认值。writesession 里需要注意下设置 session 之后需要 save。
删除 session 可以尝试如下:
del session['name'] # 删除所有 session.delete()
到这里,WEB 常用的一些东西在 Pylons 里基本走了一圈,包含 URL、模板、数据库和会话部分。
下一节将会涉及怎么在 Nginx 上发布 Pylons 应用。
前面两篇入门,讲述了 Pylons 大致开发的流程、表单以及文件上传,思路大致跟传统的开发类似。本篇简单讲述下在 Pylons 如何使用数据库。
本篇侧重点是使用 ORM 框架 SQLAlchemy。现在 Python 社区里关注度比较高的大概有三:SQLAlchemy、SQLObject 和 Storm。其实本人最早是研究了一下 Storm,后来听虾哥(@marchliu)在应用里不是很爽之,遂关注了下他推荐的 SQLAlchemy。当然,你也可以对应数据库的 DB-API 库来进行操作。
示例代码的数据库是 PostgreSQL,对应的 Python 库使用的是 psycopg2。至于 Pg 配置和使用这里不再累赘,请狗之。
Debian/Ubuntu 安装很简单:
sudo aptitude install python-psycopg2
建立一个测试数据库,比如 test:
smallfish@debian:~/workspace/python/hello$ su postgres postgres@debian:/home/smallfish/workspace/python/hello$ createdb -O smallfish test postgres@debian:/home/smallfish/workspace/python/hello$ exit smallfish@debian:~/workspace/python/hello$ psql -h 127.0.0.1 -p 5432 -U smallfish test 用户 smallfish 的口令: psql (8.4.4) SSL连接 (加密:DHE-RSA-AES256-SHA,位元:256) 输入 "help" 来获取帮助信息. test=#
数据库的部分已经OK,下面就是来倒腾 Pylons 啦。
建立新项目,加入支持数据库部分,注意 Enter sqlalchemy那个选项,默认是 False,改成 True:
smallfish@debian:~/workspace/python$ paster create -t pylons hellodb Selected and implied templates: Pylons#pylons Pylons application template Variables: egg: hellodb package: hellodb project: hellodb Enter template_engine (mako/genshi/jinja2/etc: Template language) ['mako']: Enter sqlalchemy (True/False: Include SQLAlchemy 0.5 configuration) [False]: True Creating template pylons Creating directory ./hellodb
改成 True 之后在自动生成的 development.ini 里就有对应的数据库配置选项了。
再建立新的 db controller:
smallfish@debian:~/workspace/python$ cd hellodb/ smallfish@debian:~/workspace/python/hellodb$ paster controller db Creating /home/smallfish/workspace/python/hellodb/hellodb/controllers/db.py Creating /home/smallfish/workspace/python/hellodb/hellodb/tests/functional/test_db.py
编辑 development.ini,添加数据库配置部分。smallfish:123456 是对应的 PostgreSQL 用户名/密码,127.0.0.1:5432 是对应的主机地址/端口号,最后的test则是数据库名。
# SQLAlchemy database URL sqlalchemy.url = postgresql://smallfish:[email protected]:5432/test
编辑 hellodb/model/__init__.py,加上一个叫 msg 的表和字段的定义:
"""The application's model objects""" from hellodb.model.meta import Session, metadata from sqlalchemy import orm, schema, types from datetime import datetime def init_model(engine): """Call me before using any of the tables or classes in the model""" Session.configure(bind=engine) def now(): return datetime.now() msg_table = schema.Table('msg', metadata, schema.Column('id', types.Integer, schema.Sequence('msg_seq_id', optional=True), primary_key=True), schema.Column('content', types.Text(), nullable=False), schema.Column('addtime', types.DateTime(), default=now), ) class Msg(object): pass orm.mapper(Msg, msg_table)
示例 Msg 表很简单,三个字段:ID、内容和时间。
上面的代码除去导入 sqlclchemy 包里几个库,基本上有一个对应表的字段定义,还有一个空的 Msg 对象。
最后一行,则是做一个 map 的动作,把 Msg 映射到 msg_table 上。
下面是不是要在数据库里建立对应的表呢?有个简单的办法 可以初始化数据库:paster setup-app development.ini:
smallfish@debian:~/workspace/python/hellodb$ paster setup-app development.ini Running setup_config() from hellodb.websetup 20:08:43,619 INFO [sqlalchemy.engine.base.Engine.0x...854c] [MainThread] select version() 20:08:43,619 INFO [sqlalchemy.engine.base.Engine.0x...854c] [MainThread] {} 20:08:43,625 INFO [sqlalchemy.engine.base.Engine.0x...854c] [MainThread] select current_schema() 20:08:43,625 INFO [sqlalchemy.engine.base.Engine.0x...854c] [MainThread] {} 20:08:43,631 INFO [sqlalchemy.engine.base.Engine.0x...854c] [MainThread] select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where n.nspname=current_schema() and lower(relname)=%(name)s 20:08:43,631 INFO [sqlalchemy.engine.base.Engine.0x...854c] [MainThread] {'name': u'msg'} 20:08:43,637 INFO [sqlalchemy.engine.base.Engine.0x...854c] [MainThread] CREATE TABLE msg ( id SERIAL NOT NULL, content TEXT NOT NULL, addtime TIMESTAMP WITHOUT TIME ZONE, PRIMARY KEY (id) ) 20:08:43,637 INFO [sqlalchemy.engine.base.Engine.0x...854c] [MainThread] {} 20:08:43,732 INFO [sqlalchemy.engine.base.Engine.0x...854c] [MainThread] COMMIT
可以看到上面的输出日志,包括了建表的SQL语句。其中 SERIAL 对应上面 __init__.py 里 Column 的 Seq 定义。serial 类型 在 PostgreSQL 可以看成类似 MySQL 的自增ID(auto_increment) 。
现在进入 PostgreSQL 查询数据库,就可以看到表以及序列已经建立。
test=# \d 关联列表 架构模式 | 名称 | 型别 | 拥有者 ----------+------------+--------+----------- public | msg | 资料表 | smallfish public | msg_id_seq | 序列数 | smallfish (2 行记录)
到这里,准备工作已经完毕,包括了初始化数据库,配置文件还有示例 controller。
下面就在 controller 代码里增加读写数据库的功能吧。
首先建立一个表单模板 db.htm ,用来添加并保存到数据库表中:
<form action="/db/add" method="post"> <input type="text" name="content" /> <br /> <input type="submit" value="save" /> </form>
对应 controller index 修改成,很简单。返回到模板:
class DbController(BaseController): def index(self): return render('/db.htm')
添加 add 方法,对应上面 form 中的 /db/add 路径:
def add(self): content = request.POST['content'] from hellodb import model msg = model.Msg() msg.content = content model.meta.Session.add(msg) model.meta.Session.commit() return "add %s ok..." % content
添加部分简单完成。获取 POST 文本框,然后初始化一个 Msg 对象(上面 model 里定义的)。
注意 add 之后,必须手动 commit,这样才会真正保存到数据库。
浏览器访问一下:http://127.0.0.1:5000/db/index,随意添加一点数据吧,这个时候你可以在 PostgreSQL 里查询已经数据已经加进来了。
下面在 index 方法传递一些值到模板,输出刚才已经添加的数据:
def index(self): from hellodb import model c.msgs = model.meta.Session.query(model.Msg).all() return render('/db.htm')
c.msgs 可以理解成全局变量,关于 c 的定义在 controller前几行就应该看到了。修改模板 db.htm 显示记录:
% for msg in c.msgs: <p>${msg.id}: ${msg.content} / ${msg.addtime}</p> % endfor
很简单,只是一个普通 for 循环,遍历 index 方式里传递的 c.msgs。Mako模板还是很易读的吧?
继续刷新下:http://127.0.0.1:5000/db/index,可以在页面上看到已经添加的数据了。
在狂输入了几十条之后,在一页里显示是不是忒土鳖了?
下面再介绍下 Pylons 里 webhelper 里一个分页组件的用法,当然你也可以自己写分页算法。下面是示例:
def list(self): from webhelpers import paginate from hellodb import model msgs = model.meta.Session.query(model.Msg) c.paginator = paginate.Page( msgs, page=int(request.params.get('page', 1)), items_per_page = 10, ) return render("/list.htm")
导入 paginate,然后把查询的数据库对象当参数传递给 paginate.Page,里面的page则是页面的传递的页数参数,items_per_page 就好理解多了,就是多少条一页。这里是10条。
对应的模板 list.htm 如下:
<pre> % if len(c.paginator): % for msg in c.paginator: <p>${msg.id}: ${msg.content}</p> % endfor <p> ${c.paginator.pager("count: $item_count $first_item to $last_item , $link_first $link_previous $link_next $link_last")} </p> % endif
for 部分如同上面示例,下面加了一行pager。里面一些变量从名字上就可以看出功能了。包括了总条数、当前是第几到第几条,然后就是常用的首页、上页、下页和最后一页。
这里链接的文字都是<<, <, >, >>。想改成文字请查看文档吧。。如果是第一页,是不会显示首页和上一页的。这个做过分页的一般都写过类似的代码吧。
现在访问:http://127.0.0.1:5000/db/list,想看到效果当然你得多填点数据哦。10条才会显示分页的挖。
好了,这里对数据库增加和显示部分都已经有示例代码了,当然最后还有一个分页用法。至于删除和更新之类请参考 SQLAlchemy 文档吧。
继续上一篇《Pylons 入门实例教程 – Hello》,现在开始讲在 Pylons 里如何提交表单和上传文件。
继续延用上篇里面的 hello 工程,在 HiController 里添加 form 方法:
def form(self): return render('/form.mako')
加完以后可以访问:http://127.0.0.1:5000/hi/form,会报错。
Server Error,根据报错内容大致就知道模板文件不存在了。如果有其他错误,也可以通过这个页面查看,当然还有很强大的 Debug 个功能哦。当然正式环境一般都是关闭这个功能的。这个,你懂得。。。
好吧,写一个表单的模板,只包含一个简单的文本框和提交按钮示例。
<form action="/hi/submit" method="post"> name: <input type="text" name="name" /> <br /> <input type="submit" value="submit" /> </form>
再添加一个 submit 方法来处理表单提交,
def submit(self): return "hello, name: %s" % request.params['name']
request.params 包含了表单或者URL提交的参数,建议 POST 数据参照下面的上传部分。想获取更详细的列表,可以查看文档或者自己手动 dir()查阅。
下面尝试一下文件上传,首先在 development.ini 添加一个变量,用来存放文件上传后的文件夹。
[app:main] upload_dir = %(here)s/upload
%(here) 启动后 server 会替换到当前目录地址,上面的地址就是当前路径下的upload文件夹。
修改一下刚才的表单,加一个 file 上传,注意 multipart/form-data 这句,上传必须。
<form action="/hi/submit" method="post" enctype= "multipart/form-data"> name: <input type="text" name="name" /> <br /> file: <input type="file" name="file" /> <br /> <input type="submit" value="submit" /> </form>
修改 submit 方法,添加文件内容:
def submit(self): name = request.POST['name'] myfile = request.POST['file'] import os import shutil from pylons import config local_name = os.path.join(config['app_conf']['upload_dir'], myfile.filename) local_file = open(local_name, "wb") shutil.copyfileobj(myfile.file, local_file) myfile.file.close() local_file.close() return "hello, name: %s, upload: %s" % (name, myfile.filename)
里面 import 部分这里仅仅为了示例,正式的代码请放入程序开头部分,POST 内容可以从 request.POST 获取。
config['app_conf']['upload_dir'] 就是刚才配置里 development.ini 定义的地址。这个目录需要自己手动创建哦。
smallfish@debian:~/workspace/python/hello$ mkdir upload
OK,到这里程序部分都已经修改完成。重新访问一下:http://127.0.0.1:5000/hi/form
尝试一下上传,上传后可以在 upload 文件夹下看到文件了吧。。
当然这里只是示例,还需要处理一下上传的名字,防止有特殊符号哦。
Pylons 是 Python 的一个轻量级 MVC Web 开发框架,跟另外一个框架 TurboGears 比较相似,都是集合了一些优秀的组件而成。比如对 Request URL 采用了 Route,Template 采用了 Mako,数据库层则采用了ORM SQLAlchemy,当然,这些组件只是默认,你还可以根据自己喜好来选择其他组件,比如你可以采用 Jinja2 或 Genshi 模板,ORM也可以采用 SQLObject。完全是自由组合。
废话少说,现在开始安装吧。
smallfish@debian:~$ sudo aptitude install python-pylons
Debian/Ubuntu 系列系统可以直接 aptitude 安装,当然你也可以使用 easy_install 或者源码安装。
smallfish@debian:~$ sudo easy_install Pylons
更多安装文档请参考官网安装部分,http://pylonshq.com/docs/en/1.0/gettingstarted/#installing
好了,安装结束,来一个经典的Hello程序吧。
smallfish@debian:~/workspace/python$ paster create -t pylons hello Selected and implied templates: Pylons#pylons Pylons application template Variables: egg: hello package: hello project: hello Enter template_engine (mako/genshi/jinja2/etc: Template language) ['mako']: Enter sqlalchemy (True/False: Include SQLAlchemy 0.5 configuration) [False]: Creating template pylons Creating directory ./hello
下面输出略过,大致解说一下。Pylons 程序可以用 Paste 自动生成一些代码,包括controller。还可以运行 HTTP 服务来测试。
-t 表示自动创建的模板,可以如下来查看有哪些选项,更多就参考 help 吧。
smallfish@debian:~/workspace/python/hello$ paster create --list-templates Available templates: basic_package: A basic setuptools-enabled package paste_deploy: A web application deployed through paste.deploy pylons: Pylons application template pylons_minimal: Pylons minimal application template
看一下hello的目录结构:
smallfish@debian:~/workspace/python/hello$ ls development.ini ez_setup.py hello.egg-info README.txt setup.py docs hello MANIFEST.in setup.cfg test.ini
这里具体各个文件意思就不讲解了,程序主体部分都在hello/hello目录下,development.ini 和 test.ini 分别是服务启动的配置文件,用于测试和开发环境。开始先运行一下,看效果吧。。
smallfish@debian:~/workspace/python/hello$ paster serve --reload development.ini Starting subprocess with file monitor Starting server in PID 1519. serving on http://127.0.0.1:5000
在浏览器中打开:http://127.0.0.1:5000 看到页面了吧?恭喜。
继续,写一个简单的显示 hi的 controller 程序吧。
smallfish@debian:~/workspace/python/hello$ paster controller hi Creating /home/smallfish/workspace/python/hello/hello/controllers/hi.py Creating /home/smallfish/workspace/python/hello/hello/tests/functional/test_hi.py
自动生成程序和 test 文件。paster 启动服务不需要重启会自动加载,可以浏览器访问:http://127.0.0.1:5000/hi/index
很简单吧,打开 hi.py,基本如下:
class HiController(BaseController): def index(self): # Return a rendered template #return render('/hi.mako') # or, return a response return 'Hello World'
上面注释部分可以 return 一个模板文件,模板放入 templates 目录下即可。
去除上面的 return ‘Hello’ 返回 return render(‘/hi.mako’)
smallfish@debian:~/workspace/python/hello$ vi hello/templates/hi.mako % for key, value in request.environ.items(): ${key} = ${value} % endfor
刷新 http://127.0.0.1:5000/hi/index ,可以看到一些环境变量的输出了吧。
今天就简单的说到这里吧,下回来一个完整的例子,包括URL、模板、数据库和Session的实例。