Python轻量级库web.py

我们最近要把师兄之前用web.py做的后台用Flask重新做一下, 研究了一下之前师兄写的代码, 在这里做一个总结.


先从需求入手, 这个网站是为学校的一个电脑维修俱乐部服务的, 有同学需要维修服务的可以先来网站做个简单的登记. 登记的内容包括自己的手机号和电脑遇到的问题. 然后在俱乐部会在需求量到达一定程度时集中通知这些同学我们的服务时间地点, 进行义务维修.


针对这些需求, 网站实现的功能如下:

①登陆. 用户可以直接用手机号登陆, 无需注册( 在这里我发现网站后台并没有判断手机号是否合法 ),  然后做相应的提示 ( 使用须知, 服务范围 ),  进一步的详细信息( 姓名, 校园BBS账号, 所在校区, 电脑型号, 操作系统, 问题描述). 此外, 还可以查询历史记录;

②后台管理. 查看当前需求的详细信息


首先是把URL跟action对应起来.用元组表示, 举个例子:

url=("/", "action.user.login", 
        "/login", "action.user.login",)

这指明网站根目录对应的是login这个类, 网站的login目录对应的也是login类. 这个类中需要自己定义GET, POST等自己需要实现的方法.  从web.input 取得需要的参数. 对应上面的login类就可以定义如下(这是个示例, 师兄并不是这样写的):

class login:
    def GET(self):
        i=web.input()
        name=i.name
        pwd=i.pwd
        return renderer.logined(user=name, passwd=pwd)
    def POST(self):
        i=web.input()
        name=i.name
        pwd=i.pwd
        return renderer.logined(user=name, passwd=pwd)


每个GET或POST方法都需要返回的值是一个页面对象. 

就本例来说,renderder需要这样定义, render=web.template.render, 顾名思义, 就是一个装饰器, 每次在返回页面文件是它会用定义传入的文件夹路径和模板文件把需要返回的部分"包裹起来", 如 

render=web.template.render("/templates/",base="layout")
每次GET方法访问网站根目录时, 网站会调用login类的GET方法, 取得name和pwd输入, 然后把templates文件夹下的logined.html文件( 对应logined方法 )装入layout.html文件中,最后返回这个页面.

logined.html是这样取得输入参数的:

$def with(user=None, passwd=None)
layout.html是这样"包裹" logined.html的:

$def with(content)
<html>
<head>
<title>标题</title>
</head>
<body>
$:content       <!--这对应的就是logined.html的内容-->
</body>
</html>



网站的404和500等错误定义为函数, 例子如下:

def notfound():
    return web.notfound(render_error.e404())

def internalerror():
    return web.InternalError(render_error.e500())

app.notfound = notfound
app.internalerror = internalerror


web.py中session的操作在web.session库中, 师兄保存session数据用到了redis数据库, 要保存session数据首先要继承web.session.Store类, 重载其构造函数和__contains__, __getitem__, __setitem__, __delitem__, cleanup方法,示例如下:

class RedisStore(web.session.Store):

    """Store for saving a session in redis:
    import rediswebpy
    session = web.session.Session(app, rediswebpy.RedisStore(), initializer={'count': 0})
    """
    def __init__(self, ip='localhost', port=6379, db=0, initial_flush=False):
        self.redis_server = redis.Redis(ip, port, db)
        if initial_flush:
            """
            flushing the database is very important when you update your
            Session object initializer dictionary argument.
            E.g.
            # Before Update:
            session = web.session.Session(app,
                                          rediswebpy.RedisStore(initial_flush=True),
                                          initializer={'a':1})
            # After Update:
            session = web.session.Session(app,
                                          rediswebpy.RedisStore(initial_flush=True),
                                          initializer={'a':1, 'b':2})
            # This will cause an error if initial_flush=False since existing
            # sessions in Redis will not contain the key 'b'.
            """
            self.redis_server.flushdb()
    
    def __contains__(self, key):
        # test if session exists for given key
        return bool(self.redis_server.get(SESSION+key))

    def __getitem__(self, key):
        # attempt to get session data from redis store for given key
        data = self.redis_server.get(SESSION+key)
        # if the session existed for the given key
        if data:
            # update the expiration time
            self.redis_server.expire(SESSION+key,
                                     web.webapi.config.session_parameters.timeout)
            return self.decode(data)
        else:
            raise KeyError

    def __setitem__(self, key, value):
        # set the redis value for given key to the encoded value, and reset the
        # expiration time
        self.redis_server.set(SESSION+key,
                              self.encode(value))
        self.redis_server.expire(SESSION+key,
                                     web.webapi.config.session_parameters.timeout)
                
    def __delitem__(self, key):
        self.redis_server.delete(SESSION+key)

    def cleanup(self, timeout):
        # since redis takes care of expiration for us, we don't need to do any
        # clean up
        pass
然后,进行如下初始化:

session = web.session.Session(app, RedisStore(), initializer={
    "name": None,
    "ip": None,
    })

def session_hook():
    web.ctx.session = session

app.add_processor(web.loadhook(session_hook))
使用的时候调用从web.ctx.session取得session, 再进行相关操作

网站上保存用户的相关数据用到了mysql数据库,定义如下:
dbconfig = {
    "dbn":      'mysql',
    "db":       '数据库名称',
    "user":     '用户名',
    "pw":       '密码',
    "host":     'URL地址',
    "port":     '端口号',
    "pooling":  '是否设置数据库池,True或False',
}

mydb = web.database(**dbconfig)

web.py对mysql的数据库操作可以参考http://www.liaoxuefeng.com/article/001373891312159987278f8f31248fd9ad8aca21a3f0e6b000, 基本上看了就会用了.


数据库中设置了三个表, 分别是members, order, reply三个表, members表中保存的是后台管理人员的相关信息, order表保存同学的预订信息, reply保存的是维修过的回复信息.

这里师兄数据库表的设置存在问题. 首先order表中保存的信息既包括已经处理的, 也包括尚未处理的; 其次数据库并没有单独列出一个单独的表保存用户的个人信息, 因此在查询历史记录时需要从order表中找信息; 可以说order表成了一个大杂烩. 所有的信息都要从order表中提取. 第三order表中已经有reply字段了, 但又建立了reply表, 存在不必要的信息冗余.

就我的看法,数据库可以这样设计( 仅仅列出要点 ):

①. 在数据库中建立manager, user, order, processed_order表;

②. manager保存后台管理人员的信息, user表保存所有登记过的用户的个人信息, order表记录已经提交但尚未处理的请求, processed_order表记录已经处理的请求.

③. order表中要有userid外键指向user表的userid, 用户的需求信息;

④. processed表中除了order表中的字段外, 再添加 "维修结果" , "原因说明" 和 "给用户的建议" 字段. 



web.py类似这样运行:

app=web.application(url, globals())

if __name__="__main__"
    app.run()

此处的url变量定义URL和action的对应关系, 具体在上面已经讲过了, 在app.run()之后网站就可以访问到了.


打开终端,输入如下值:

python login.py

此时在当前系统下可以访问http://localhost:8080.


但是看到这里还是存在问题, 网站只能在本地被访问, 如果要远程访问需要搭建web服务器, 可以用Apache或者Nginx, 还需要中间件连接web服务器和web程序, 学长用的是uWSGI, 它实现了uwsgi, WSGI和http协议, Nginx可以通过HttpUwsgiModule模块与uWsgi交互.

uWSGI调用web程序的方法如下:

import app            #app就是上文中提到的app
application = app.wsgifunc()


Nginx设置中相关设置参考http://developer.51cto.com/art/201010/229615.htm, 我对Nginx还不是太熟, 要仔细研究研究.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值