Pylons 入门实例教程 – 发布应用

前面几篇教程简单讲述了如何使用 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 进程,或参考搜狐邮箱使用的 http://mailteam.blog.sohu.com/136961251.html 来管理进程):

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
Published: 2011-02-19 — Comments

PostgreSQL Key-Value 数据类型 hstore 使用教程

现在满地都是KV数据库的文字,PostgreSQL 也有类似的结构。不过是通过其强大的扩展方式实现的。

官网文档请参考:http://www.postgresql.org/docs/current/static/hstore.html

本文测试环境在 Mac OS 下,Pg采用源码编译。

编译 hstore 扩展

mac:~ smallfish$ cd Downloads/postgresql-9.0.1/contrib/hstore/
mac:hstore smallfish$ make
... 一堆编译信息
mac:hstore smallfish$ sudo make install

导入到数据库中,注意必须以 postgres 用户,如果需要装入到指定数据库,请指明。这里采用默认数据库。

mac:hstore smallfish$ ls /opt/postgresql/share/contrib/
hstore.sql     uninstall_hstore.sql
mac:hstore smallfish$ psql -U postgres -f /opt/postgresql/share/contrib/hstore.sql
... 一堆导入命令

进入数据库,建一个测试表

postgres=# CREATE TABLE testhstore (id SERIAL, value hstore);
NOTICE:  CREATE TABLE will create implicit sequence "testhstore_id_seq" for serial column "testhstore.id"
CREATE TABLE

查看下表结构

postgres=# \d
                List of relations
 Schema |       Name        |   Type   |  Owner
--------+-------------------+----------+----------
 public | testhstore        | table    | postgres
 public | testhstore_id_seq | sequence | postgres
(2 rows)

postgres=# \d testhstore;
                         Table "public.testhstore"
 Column |  Type   |                        Modifiers
--------+---------+---------------------------------------------------------
 id     | integer | not null default nextval('testhstore_id_seq'::regclass)
 value  | hstore  |

尝试下简单 hstore类型

postgres=# select 'a=>1, b=>2'::hstore;
       hstore
--------------------
 "a"=>"1", "b"=>"2"
(1 row)

postgres=# select 'a=>1, b=>a'::hstore;
       hstore
--------------------
 "a"=>"1", "b"=>"a"
(1 row)

写几条测试数据先

postgres=# INSERT INTO testhstore (value) VALUES ('name=>smallfish, age=>29'::hstore);
INSERT 0 1
postgres=# SELECT * FROM testhstore;
 id |              value
----+----------------------------------
  1 | "age"=>"29", "name"=>"smallfish"
(1 row)

postgres=# INSERT INTO testhstore (value) VALUES ('name=>nnfish, age=>20'::hstore);
INSERT 0 1
postgres=# INSERT INTO testhstore (value) VALUES ('name=>aaa, age=>30, addr=>China'::hstore);
INSERT 0 1

查看下所有数据

postgres=# SELECT * FROM testhstore;
 id |                    value
----+---------------------------------------------
  1 | "age"=>"29", "name"=>"smallfish"
  2 | "age"=>"20", "name"=>"nnfish"
  3 | "age"=>"30", "addr"=>"China", "name"=>"aaa"
(3 rows)

查询列里面的指定 key

postgres=# SELECT id, value->'name' AS name FROM testhstore;
 id |   name
----+-----------
  1 | smallfish
  2 | nnfish
  3 | aaa
(3 rows)

postgres=# SELECT id, value->'name', value->'age' AS age FROM testhstore;
 id | ?column?  | age
----+-----------+-----
  1 | smallfish | 29
  2 | nnfish    | 20
  3 | aaa       | 30
(3 rows)

修改列某 key 值

postgres=# UPDATE testhstore SET value=value||('addr=>Shanghai') WHERE id = 2;
UPDATE 1

postgres=# SELECT * FROM testhstore;
 id |                       value
----+---------------------------------------------------
  1 | "age"=>"29", "name"=>"smallfish"
  3 | "age"=>"30", "addr"=>"China", "name"=>"aaa"
  2 | "age"=>"20", "addr"=>"Shanghai", "name"=>"nnfish"
(3 rows)

删除列里某 key

postgres=# UPDATE testhstore SET value=delete(value, 'addr') WHERE id = 3;
UPDATE 1

postgres=# SELECT * FROM testhstore;
 id |                       value
----+---------------------------------------------------
  1 | "age"=>"29", "name"=>"smallfish"
  2 | "age"=>"20", "addr"=>"Shanghai", "name"=>"nnfish"
  3 | "age"=>"30", "name"=>"aaa"
(3 rows)

按条件查询列里某 key ,注意要数据类型,CAST 强转。默认都是字符串的值。

postgres=# SELECT * FROM testhstore WHERE (value->'age')::INT > 20;
 id |              value
----+----------------------------------
  1 | "age"=>"29", "name"=>"smallfish"
  3 | "age"=>"30", "name"=>"aaa"
(2 rows)

postgres=# SELECT * FROM testhstore WHERE value->'name' = 'smallfish';
 id |              value
----+----------------------------------
  1 | "age"=>"29", "name"=>"smallfish"
(1 row)

 

Published: 2011-02-19 — Comments

2010 – 继续前行的路上

每一天都在前行的路上,而这一年似乎去的特别的快,特别的短暂。

一年的时间辗转三个城市,从南京到杭州,再到上海。一切的一切似乎都是昨天,那般的清晰。

其实很早就想离开工作快五年的公司,只不过是自己一直下不了决心。性格和患得患失一直左右着自己的思绪。时间的流逝,青春不再,或许再过些时间,或许什么都不是了。

偶然的机会,很荣幸的进入淘宝工作。能够从自己的小圈子走出来,去接触外面的世界。短短的几个月中,成长的内容明显的超过了前几年的积累。这不仅仅是技术的成长,更多的是人,是视野,是想法。这一切的一切,都得感谢淘宝的师兄和主管。回想前几年,自己的世界里只有去完成一些事情,而现在更多的是应该觉得自己主动解决一些事情。当然,沟通上面来的更为明显。程序久了,思维的方式都会定死,而接触其他的岗位,去了解他们人的思路和想法,很多思绪都会豁然开朗。

杭州之行匆匆而逝,来到了大上海,这个充满魔力和摩登的都市。说真的,自己尚未体会。换了新环境,适应是必须的。在试用期内很快直接切入项目,几个月很快过去了。虽然在一些问题上自己不能决定什么,但是还是很幸运,可以从项目最初的雏形,到现在已经测试发布,自己都在其中。其实这个正是自己所擅长的。我想的就是自己能够决定更多,能够参与的更多,当然,或许这个可能性还需要自己去努力,或许现实总是那般无力,或许。

一年的流水账,想说,并不容易。希望自己一直能够如此前行,走自己的路,知道自己需要什么,能够得到什么。足矣。

零点的钟声已过,回忆这一年,许许多多的人,给了自己很多很多的帮助,感谢一下。

何伟平:很感谢何老师的推荐,才能有机会进入淘宝这样的大公司。何老师的谦虚和热心,很值得自己去学习。很遗憾,至今未有机会请何老师吃顿饭。

康伯:在淘宝时候我的主管。很感谢他跟自己多次的交流,有想法就说,就交流。给了自己很多机会。他是我至今为止唯一一个在系统方面很膜拜的人。

大舞:淘宝入职时候的师兄。试用期内给了自己很多建议和指导,一个很帅很帅的男人。@kemin_ukl

春哥:非芒果台的那位。感谢春哥推荐了自己来盛大,还混了不少饭,春哥工作和生活的太多很值得码农学习和借鉴。@mcspring

道神:灰常牛叉的DD,感谢刚来魔都时候收留了俺这个码农。回头还要多搭讪,求其在Debian方面的经验。@lidaobing

群众:鄙人群里那些哥们,你们都是神一样的男人!因为有了你们,才有了欢乐!@marchliu @xiaoxiaolu @guangfengio 等等大牛们。

话不多说,祝愿那些把帮助和关心过我的人,新年能够身体健康,幸福快乐!

去迎接新的一年,我,继续在路上。

 

Published: 2011-02-19 — Comments

开源网页截屏工具 CutyCapt Linux 下安装和使用

目的是想在服务器端生成某个网页的缩略图。Google了好久,发现一个好开源东西:CutyCapt。

系统:CentOS 5.5 官网:http://cutycapt.sourceforge.net/ 依赖:QT http://www.qtsoftware.com/

首次是在Debian下测试的,很顺利。换了CentOS不是太爽。具体安装和使用步骤如下:

  1. 下载RPM包
(64)
wget http://dl.atrpms.net/el5-x86_64/atrpms/testing/qt44-4.4.3-10_4.el5.x86_64.rpm
wget http://dl.atrpms.net/el5-x86_64/atrpms/testing/qt44-x11-4.4.3-10_4.el5.x86_64.rpm
wget http://dl.atrpms.net/el5-x86_64/atrpms/testing/qt44-devel-4.4.3-10_4.el5.x86_64.rpm

(32)
wget http://ftp.riken.go.jp/Linux/atrpms/el5-i386/atrpms/testing/qt44-4.4.3-10_4.el5.i386.rpm
wget http://ftp.riken.go.jp/Linux/atrpms/el5-i386/atrpms/testing/qt44-x11-4.4.3-10_4.el5.i386.rpm
wget http://ftp.riken.go.jp/Linux/atrpms/el5-i386/atrpms/testing/qt44-devel-4.4.3-10_4.el5.i386.rpm
  1. 安装 qt-devel 依赖包
yum install libXi-devel
yum install libXinerama-devel
  1. 安装 qt 相关
rpm -ivh qt44-4.4.3-10*
rpm -ivh qt44-x11-4.4.3-10*
# rpm -e qt-devel --nodeps --allmatches
rpm -ivh qt44-devel-4.4.3-10*
  1. 修改 /etc/profile,最后并:source /etc/profile
export QTDIR=/usr/lib64/qt44
export QTLIB=/usr/lib64/qt44/lib
export QTINC=/usr/lib64/qt44/include
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
export PATH=$QTDIR/bin:$PATH
  1. 安装 cutycapt
svn co https://cutycapt.svn.sourceforge.net/svnroot/cutycapt
mv cutycapt/CutyCapt /usr/local/CutyCapt
cd /usr/local/CutyCapt
qmake
make
  1. 安装模拟 x-server 服务端
wget http://www.flexthinker.com/wp-content/uploads/2009/11/xvfb-run.sh.txt
mv ./xvfb-run.sh.txt /usr/local/CutyCapt/xvfb-run.sh
chmod u+x /usr/local/CutyCapt/xvfb-run.sh
  1. 开始欢快的截图吧
/usr/local/CutyCapt/xvfb-run.sh --server-args="-screen 0, 1024x768x24" /usr/local/CutyCapt/CutyCapt --url=http://www.163.com --out=163.jpg
  1. 如果看不到汉字或乱码,需要安装chinese字体
yum install fonts-chinese
  1. 由于截屏的是整个网站的页面,只需要第一屏幕
convert -crop 1024x768+0+0 163.jpg 1632.jpg
  1. 缩小图片
convert -resize 40%x40% 1632.jpg 1632.jpg
Published: 2011-02-19 — Comments

Pythonic 分享

很荣幸收到龙哥 @hoorace 邀请去参加了杭州第四期程序员圆桌会议,顺带介绍了下最近在公司内部做的关于《Pythonic》分享。喜欢Python的可以下载看看把。

Pythonic[http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=pythonic-100807044344-phpapp02&stripped_title=pythonic&userName=nnfish](http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=pythonic-100807044344-phpapp02&stripped_title=pythonic&userName=nnfish) 
View more [http://www.slideshare.net/](http://www.slideshare.net/).

收获还是颇丰,多接触了点其他工种的朋友。

话题比较宽泛,基本我就是纯酱油飘过。感谢网新的朋友做的Vim的分享,另外威猛的升哥topic也不错,对RedHat系统安装和启动加载有了更多的了解。

另外很遗憾的就是威大师竟然没说点啥,可惜了那么好的水果茶啊。。。残念残念。。

Published: 2010-08-07 — Comments

Pylons 入门实例教程 – cookie 和 session

本篇讲述在 Pylons 里使用 cookie 和 session。

示例还是在上篇《Pylons 入门实例教程 – cookie 和 session》的代码里继续添加。先来尝试下  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 应用。

Published: 2010-07-03 — Comments

Pylons 入门实例教程 – 数据库操作

前面两篇入门,讲述了 Pylons 大致开发的流程、表单以及文件上传,思路大致跟传统的开发类似。本篇简单讲述下在 Pylons 如何使用数据库。

本篇侧重点是使用 ORM 框架 http://www.sqlalchemy.org/。其实本人最早是研究了一下 Storm,后来听虾哥(@marchliu)在应用里不是很爽之,遂关注了下他推荐的 SQLAlchemy。当然,你也可以对应数据库的 DB-API 库来进行操作。

示例代码的数据库是 http://www.postgresql.org。至于 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:123456@127.0.0.1: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 文档吧。

Published: 2010-07-01 — Comments

Pylons 入门实例教程 – 表单和文件上传

继续上一篇《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 文件夹下看到文件了吧。。

当然这里只是示例,还需要处理一下上传的名字,防止有特殊符号哦。

Published: 2010-06-30 — Comments

如何写 Go 代码?

原文:http://golang.org/doc/code.html

简述

这篇文档描述了如何去写一个新的 package 和怎么去测试。本文假设你已经按照http://golang.org/doc/install.html装好Go。

在修改一个存在的 package 或者新建立一个package,确保先发一封邮件到 http://groups.google.com/group/golang-nuts,告诉大家你想做什么。这样有助于不要重复造轮子,在写代码之前最好讨论下。

社区资源

如果想获取实时帮助,可以加入 http://freenode.net/ 上 IRC 频道 #go-nuts。

Go 语言官方邮件列表是 http://groups.google.com/group/golang-nuts.

Bugs 可以参考http://code.google.com/p/go/issues/list.

对于那些想尝试开发代码的用户,这里有另外一个邮件列表 http://groups.google.com/group/golang-checkins,邮件里包含了那些刚提交到 Go 代码库的消息。

建立Go包

下面的源码假设 package 的导入路径是 x/y,约定下保存的路径是:$GOROOT/src/pkg/x/y

Makefile

这将是很好的利用 Go-specific 工具里安排源码结构,如何按照顺序和构建代码。Go 使用 GUN make。所以首先在一个新的package 文件夹里建立一个 Makefile。最简单的做法就是从 http://golang.org/src/pkg/container/vector/Makefile 源码包里拷贝一份。

include ../../../Make.$(GOARCH)

TARG=container/vector
GOFILES=\
    intvector.go\
    stringvector.go\
    vector.go\

include ../../../Make.pkg

当然在上面的源码包之外写一个新的 package ,通常的 Makefile 如下:

include $(GOROOT)/src/Make.$(GOARCH)

TARG=mypackage
GOFILES=\
    my1.go\
    my2.go\

include $(GOROOT)/src/Make.pkg

第一行 include 标准的定义和规则,package 的路径一般相对路径来代替 $(GOROOT)/src ,这样做到目的就是防止 make 时候 $(GOROOT) 含有空格,这样做很方便开发中使用Go。

TRAG 是包安装路径,这行就是其他程序导入到名字。在 Go 源码中,这个名字必须和文件夹中的 Makefile 中的一致,不需要 $GOROOT/src/pkg/ 前缀。在源码之外,你可以任意起名而不要和标准 package 的重名即可。常见的做法是独立一个 package 名:myname/tree, myname/filter,确保你的代码在 myname 里,运行 make install,将会编译后把二进制文件放入 $GOROOT/pkg,可以很方便的找到。

GOFILES 放入文件内需要编译的源码列表。多行用 \ 字符隔开。

如果想在 Go 源码树中新建包,需要添加列表到 $GOROOT/src/pkg/Makefile 中作为标准库编译,编译运行:

cd $GOROOT/src/pkg
./deps.b

更新依赖文件 Make.deps (这是每次 make all 或者 make build 都需要)

Go 源文件

在每个源码里第一行的名字应该是 Makefile 里面定义的 package 名,这里的 name 是作为默认的名字导入。(包里的每个 package 名都应该是同样的名字)Go的惯例是用路径的最后一个元素作为名字,比如 "crypto/rot13" 应该是 rot13。到目前为止,Go 是依靠 package 名字来确定一个二进制的文件,当然可能会很快的取消。

Go 是在一个包内编译源码文件,在一个文件定义常量,变量,类型或者函数,在其他文件内适用不需要再定义和声明。

想写一个干净而优美的 Go 代码不在本文范围。请参考http://golang.org/doc/effective_go.html吧。

测试

Go 有一个轻量级的测试代码框架:gotest。编写一个测试代码,只需要新建一个跟你源码名字一样,加上:_test.go 即可,测试函数一般是 func TestXXX (t *testing.T)。每次运行测试其中的函数。如果想返回错误的话一般是返回: t.Error 或 t.Fail,测试时候就会抛错。详细 gotest 测试方法请参考:http://golang.org/cmd/gotest/

*_test.go 不需要在 Makefile 中声明。

运行测试,编译时候 make test 即可,和 gotest 效果一样。如果想单独测试某一个源码,比如:one_test.go,则运行:gotest one_test.go。

如果你的修改影响性能,可以添加一个性能测试函数(参考:http://golang.org/cmd/gotest/),然后运行:gotest -benchmarks=.

当你新的代码已经通过测试,也能运行,你就可以 review 和 commit.  http://golang.org/doc/contribute.html

一个测试的例子

这是一个名字叫 numbers 测试 package ,定义了一个叫 Double 的函数,返回一个整型,结果是输入的参数乘以2。一共有三个文件。

第一个是 numbers.go

package numbers

func Double(i int) int {
    return i * 2
}

第二个是测试文件 numbers_test.go

package numbers

import (
    "testing"
)

type doubleTest struct {
    in, out int
}

var doubleTests = []doubleTest{
    doubleTest{1, 2},
    doubleTest{2, 4},
    doubleTest{-5, -10},
}

func TestDouble(t *testing.T) {
    for _, dt := range doubleTests {
        v := Double(dt.in)
        if v != dt.out {
            t.Errorf("Double(%d) = %d, want %d.", dt.in, v, dt.out)
        }
    }
}

最后是 Makefile 文件

include $(GOROOT)/src/Make.$(GOARCH)

TARG=numbers
GOFILES=\
    numbers.go\

include $(GOROOT)/src/Make.

运行 make install 将会构建和安装包到 GOROOT/pkg/ 文件夹内(他在系统的任何地方导入调用)。

运行 make test (或者 gotest)将会重新构建包,并会运行 numbers_test.go 中的 TestDouble 函数。如果输出 "PASS" 则表示测试成功通过。如果不是2到3的倍数,将会看到错误报告。

更详细的请查看http://golang.org/cmd/gotest/文档。

Published: 2010-06-29 — Comments