web.py 学习笔记,web.py,web.py学习笔记 (


web.py学习笔记 (一)

写在前面:

前段时间打算和一个朋友做一个网站,后来另一个罗马尼亚朋友加入做开发,但是因为种种原因又退出了。我们俩人的编程能力都很弱,几个月了网站还是停留在草图阶段。

之前主要在Google app engine上玩,但是限制太多,所以一直想自己用Django做点东西。折腾过一段时间的Pinax(因为听说这个做东西比较快),后来还是卡在后端编程上,没开发完成。

现在我们决定用web.py来做这个网站。这两天照着 http://webpy.org 的上的文档写了一下,觉得比Django简单些。

另外还参考了 http://wiki.woodpecker.org.cn/moin/WebPyCssFile

感觉web.py的文档资料比较少,没有Django那么完善,也没有比较成熟的开源案例可以学习,只能自己摸索着来。

学习中遇到的问题:

在webpy.org和其他很多关于web.py的tutorial中,有这么一段:

CREATE TABLE todo (  id serial primary key,  title text,  created timestamp default now(),  done boolean default 'f'    );

这是一段创建数据表的sql语句,当时我在phpmyadmin里怎么执行都不成功,后来问了以前的同事,才知道 done boolean default ‘f’ 已经不支持了,应该是 done boolean default ’0′ ,可能是我mysql版本的问题。

2.按照 http://wiki.woodpecker.org.cn/moin/WebPyCssFile 写完之后,访问页面发现报错:

<type 'exceptions.SyntaxError'> at /invalid syntax Template traceback: File 'templates/index.html', line 13<h1>CSS Test</h1>(index.html, line 13) Python /Library/Python/2.7/site-packages/web.py-0.37-py2.7.egg/web/template.py in compile_template, line 911 Web GET http://0.0.0.0:8080/

google查了很久没搜到结果,后来给文章作者发邮件询问是不是我的web.py版本问题?结果没等他回信,我就自己解决了。我先尝试把所有的tab缩进改成空格缩进,无果,然后又把所有的空格缩进改成tab缩进,还是无果,后来把.py文件头部的

#!/usr/bin/env python# coding: utf-8

挨个删掉,每删一个就看看是不是正常了,结果全删完了报错了,说什么编码有问题,然后又一个个往回加,调着调着就行了。

webpy note image

现在的目录结构:

webpy directory structure

好了,现在怎么让页面呈现出来已经搞定,接下来准备着手写用户注册页面。

这时候又遇到一个棘手的问题,mysql里怎么建用户表?什么int、varchar我都不知道有什么区别,以前没有这方面的经验,都是其他同事做。在网上找了找资料,大概建成了这样:

webpy note image

接下来遇到的问题就是如何对数据进行增删改查,这方面更没有经验,等天亮之后问问同事看。

总结:

web.py的开发速度比Django快多了,之前看Djando的文档,一直卡在模版处理那一块儿,web.py的模版处理对我来说相对简单一些,听说还可以结合mako模版引擎来处理,但我决定暂时不用,先用最原始的办法解决问题。

最终的效果(未加CSS样式):webpy note effect

web.py学习笔记 (二)

今天在同事的帮助下完成了mysql用户表的创建,还不是很好。

同事给的比较好的用户表是这么建的:

CREATE TABLE IF NOT EXISTS `xch_account` (  `id` mediumint(8) unsigned NOT NULL auto_increment,  `email` varchar(100) NOT NULL default '',  `password` char(32) NOT NULL default '',  `firstname` varchar(50) NOT NULL default '',  `lastname` varchar(50) NOT NULL default '',  `city` varchar(50) NOT NULL default '',  `address` varchar(200) NOT NULL default '',  `tel` varchar(100) NOT NULL default '',  `mobile` varchar(100) NOT NULL default '',  `postcode` varchar(100) NOT NULL default '',  `qq` varchar(100) NOT NULL default '',  `regip` varchar(15) NOT NULL default '',  `lastip` varchar(15) NOT NULL default '',  `regtime` int(10) unsigned NOT NULL default '0',  `lasttime` int(10) unsigned NOT NULL default '0',  `loginnum` smallint(5) unsigned NOT NULL default '0',  `ukey` char(32) NOT NULL default '',  `lev` tinyint(1) unsigned NOT NULL default '0' COMMENT '0=>普通用户 1=>VIP用户',  `discount` decimal(2,1) unsigned NOT NULL default '1.0',  `verify` tinyint(1) unsigned NOT NULL default '0',  `auto` tinyint(1) unsigned NOT NULL default '0' COMMENT '自动出单',  PRIMARY KEY  (`id`),  UNIQUE KEY `email` (`email`),  UNIQUE KEY `ukey` (`ukey`),  KEY `regtime` (`regtime`)) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

我的表结构现在是这样:webpy note

不管怎么说,反正能用了。今天还做了注册页面、登录页面和登录成功后的欢迎页面,然后就开始写用户注册时的数据存储。

class SignupHandler:    def GET(self):        # do $:f.render() in the template        f = register_form()        return render.signup(f)    def POST(self):        f = register_form()        name = web.input(name = "username")        pwd = web.input(name = "password")        if not f.validates():            return render.signup(f)        else:            sequence_id = db.insert('userAuth', username = name, password = pwd, joindate = web.SQLLiteral("NOW()"), ipaddress = "111.1.1.1", lastactivity = web.SQLLiteral("NOW()"))            raise web.seeother('/welcome')

悲催的是发现这么写数据库里存的数据都不正常:

后来在V2EX上发帖求教,同时自己找问题解决办法,结果V2EX上还没等人回应,我自己就找到错误原因了。原来需要把

name = web.input(name = "username")pwd = web.input(name = "password")

改成:

name = web.input().usernamepwd = web.input().password

这样数据库里存储的文本就正常了。

web.py学习笔记 – 03

鉴于整个用户模块比较复杂,我找到了一个开源的web.py站点“MLSS”来做参考,打算以此为基础进行改进。

MLSS中跟user相关的主要文件有controllers部分的 /app/controllers/account.py 和model部分的 /app/models/users.py 以及view部分的 /app/views/account.html。我的理解,user主要的功能有登录 / 注册 / 找回密码 / 退出,在MLSS中,分别对应了 /app/controllers/account.py中的 login / register / resend_password / logout。

在测试过程中发现MLSS的用户密码没有加密,比如用户注册时填写的密码是“111111”,保存到数据库中还是“111111”,感觉好坑爹!不行,这个得改!于是我着手给密码进行加密。

我感觉应该是在 model 部分的 /app/models/users.py 中做入库之前的加密,找到一看,发现坑爹的代码:

def create_account(email, password, nickname):    db.insert('users', email=email, password=password, nickname=nickname)

直接就 password=password 了!于是我就改成了:

password=hashlib.md5(password + encryption_key).hexdigest()

这段加密的代码也是我从cookbook上找来的。原代码是:

pwdhash = hashlib.md5(i.password).hexdigest()

稍微变通一下就可以啦。

encryption_key 是 一串密钥,在 config.py 中。

# used as a saltencryption_key = 'a random string'

这样对密码进行之后,登录那边的验证也要跟着改:

def is_correct_password(email, password):    user = get_user_by_email(email)    return user.get('password', False) == password

改为了:

def is_correct_password(email, password):    user = get_user_by_email(email)    pwdhash = hashlib.md5(password + encryption_key).hexdigest() #密码混合密钥进行md5加密    return user.get('password', False) == pwdhash #判断密码是否正确

这样就完成啦,不过我还是觉得不太满意,用户的IP地址还没有呢,于是我又琢磨怎么得到用户的IP地址,google之后看到 cookbook 中有讲用 web.ctx获得IP,但是我以为是这么写:

ip = web.ctx.ip.get('HTTP_REFERER')

或者是:

ip = web.ctx.ip.get()

结果发现都不对,后来在“blog-with-gae”中有了发现,原来应该是这样:

ip = web.ctx.ip

OK,又搞定了一个。

接下来我想做用户的找回密码功能,因为MLSS的找回密码也很坑爹,直接把密码发到邮箱里了:

#-------/app/controllers/account.py------    user = users.get_user_by_email(f.d.email)    email_templates.resend_password(user)    return render_account(show,         on_success_message='Login information succesfully emailed.')#---------------------#-------/app/helps/email_templates.py------msg_forgot = \'''$def with (user)Hello $user.nickname, Here are the login information you have requested:login: $user.emailpassword: $user.passwordThank you,'''#---------------------

咨询了以前的同事,他说用户需要一个唯一的key,然后用这个key组合成重置密码的url发给用户,还需要给url设置过期时间。

一步步来:先做key。我想的是用10位随机数 + 时间 + 用户邮箱地址 + 站点密钥 进行加密作为key。代码如下:

#-------/app/models/user.py-------    #生成10位随机数    all = list('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSQUVWXYZ')    randStr = ''    for i in range(10):        index = random.randint(0,len(all))        randStr = randStr + all[index]    currTime = time.strftime('%Y%m%d%H%I%M%S',time.localtime(time.time()))    #加密:随机数 + 时间 + 邮箱地址 + 密钥. //authKey为找回密码用,每个用户都有一个唯一的authKey    authKey = hashlib.md5(randStr + currTime + email + encryption_key).hexdigest()#---------------------

当时找生成随机数的方法找了好长时间(Python基础不行真是太费事了),最终注册部分的代码是这样:

#-------/app/models/user.py-------#!/usr/bin/env python# coding: utf-8import randomimport timeimport hashlibimport webimport socketfrom config import db , encryption_keydef create_account(email, password, nickname):    #生成10位随机数    all = list('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSQUVWXYZ')    randStr = ''    for i in range(10):        index = random.randint(0,len(all))        randStr = randStr + all[index]    currTime = time.strftime('%Y%m%d%H%I%M%S',time.localtime(time.time()))    #加密:随机数 + 时间 + 邮箱地址 + 密钥. //authKey为找回密码用,每个用户都有一个唯一的authKey    authKey = hashlib.md5(randStr + currTime + email + encryption_key).hexdigest()    #得到客户端IP地址    ipAddress = web.ctx.ip    #入库        db.insert('users', email=email, password=hashlib.md5(password + encryption_key).hexdigest(), nickname=nickname, authKey = authKey, ipAddress = ipAddress)#注:password 为混合密钥进行md5加密

下面的登录退出部分就先省略了,另外,还修正了很长时间没有解决的一个Bug:

class Redirect:#Make url ending with or without '/' going to the same class.    def GET(self, path):        web.seeother('/' + path)

我始终不知道这段代码加在哪儿,就随手放config.py里了,后来发现没有起作用。刚才看“blog-with-gae”的代码,才发现原来需要在url设置中加上:

'/(.*)/', config.Redirect,

才可以啊!

总结

多看别人的代码可以学到很多东西!接下来要继续完善找回密码部分。

评论关闭