在Python3下使用tornado和SQLAlchemy实现一个简单的MVC网站

        本文讲述了在Python3下如何使用tornado框架及sqlalchemy对MySQL数据库进行简单的增、删、改、查操作。整个流程走通之后,就可以在实际开发中对本文中的例子进行重构、优化,以满足产品需要。

        环境:Window7 x64、MySQL 5.6.21、Python 3.4.1、tornado 4.0.2、MySQL-Connector-Python 2.0.1、SQLAlchemy 0.9.8

        以上这些软件的安装方法在我的另一篇博文里,如有需要请移步至:构建一个完整的基于Python3的Web开发环境。

        在本文的例子中,我们维护一个用户数据库,数据库中包含了用户名、年龄、性别、得分、主修专业。可以通过WEB页面来增加、删除用户,以及修改、显示所有的用户信息。

一、在MySQL中建立数据库及相关表

        我们的重点是用最简单的步骤实现整个MVC流程,所以建立数据库的操作就不使用SQLAlchemy来做了。

        先在Windows的命令提示符下连接到MySQL,并建立一个名为UserManager的数据库。命令如下所示:

e:\>mysql -u root -p
password:********
mysql>CREATE DATABASE UserManager;
        注意第一行-u后面的root是数据库的用户名,应当按照实际情况输入。password后面的密码也要按照你自己系统的实际情况填写。

        然后键入命令 USE UserManager 进入到UserManager数据库中,新建一个如下结构的表user:

CREATE TABLE user(
        id int(16) not null primary key auto_increment,
        user_name varchar(32) not null,
        user_age int(3) not null,
        user_sex int(1) not null,
        user_score int(3) not null,
        user_subject varchar(32) not null );
        之后我们所有的操作都是针对这张表进行的。

二、建立HTML模板页

        我们使用tornado内置的模板进行HTML的渲染,速度虽然不是最快的,但是简单实用。熟练之后可以用Jinja2等更专业的模板替换掉tornado内置的模板。

        tornado内置模板的语法非常简单,语法块默认由{% ... ... %}包含,变量则由{{ ... ... }}包含。当然也以设置为其它样式的Limiter。

        本文的例子中包含两个WEB页面:UserManager.html以及EditUserInfo.html。下面分别是这两个页面的代码。

        页面 UserManager.html:



    
    {{title}}

Welcome to UserManager


Name: Age: Sex: Score: Subject:

{% for user in users %} {% end %}
Name Age Sex Score Subject
{{ user.user_name }} {{ user.user_age }} {{ user.user_sex }} {{ user.user_score }} {{ user.user_subject }} Edit Delete
        上面这个文件把数据库中的用户信息以表格的方式输出到页面上。并提供了增加新用户的表单,以及编辑用户信息、删除用户的链接。大概是下图这个样子。

在Python3下使用tornado和SQLAlchemy实现一个简单的MVC网站_第1张图片








        页面 EditUserInfo.html:



    
    Edit Info for User: {{user_info.user_name}}


Edit User Info


Name: Age: Sex: Score: Subject:

        上面这个文件中,服务器端根据之前传来的user_name在数据库中查到该用户的所有信息,并以一个user_info的对象返回到页面上。页面表现差不多是下图这个样子:


        点击提交后,修改后的信息将自动在数据库里面更新。注意Name这个字段是不应该被修改的,所以这里虽然显示为一个TEXT文本框,但是在代码中使用了readonly的属性,该文本框实际上是不能被修改的。

        这两个文件放在项目目录的templates下,以备使用。

三、使用SQLAlchemy编写ORM层

        在正式用tornado写Server主程序之前,我们还要编写一个基本的ORM(对象关系模型)层,来做一些最底层的SQL查询、删改操作。ORM则可以为我们提供一种最自然的方式,即对象映射方式来操作数据表。对数据表的操作实际上就变成了对指定的对象进行操作,数据表中的字段都被映射到对象的属性上面。换句话说,对象属性的变化将直接更新到对应的数据表中去。

        以下是ORM层的Python代码,命名为orm.py,并存储在项目目录下。

#!/usr/bin/env python

from sqlalchemy import *
from sqlalchemy.orm import *

# Settings to connect to mysql database
database_setting = { 'database_type':'mysql',    # 数据库类型
                'connector':'mysqlconnector',    # 数据库连接器
                'user_name':'root',              # 用户名,根据实际情况修改
                'password':'abcdefg',            # 用户密码,根据实际情况修改
                'host_name':'localhost',         # 在本机上运行
                'database_name':'UserManager',
                }

# 下面这个类就是实体类,对应数据库中的user表
class User( object ):
        def __init__( self, user_name, user_age,
                        user_sex, user_score, user_subject ):
                self.user_name = user_name
                self.user_age = user_age
                self.user_sex = user_sex
                self.user_score = user_score
                self.user_subject = user_subject

# 这个类就是直接操作数据库的类
class UserManagerORM():
        def __init__( self ):
                '''
                    # 这个方法就是类的构造函数,对象创建的时候自动运行
                '''
                self.engine = create_engine(       # 生成连接字符串,有特定的格式
                                database_setting[ 'database_type' ] +
                                '+' +
                                database_setting[ 'connector' ] +
                                '://' +
                                database_setting[ 'user_name' ] +
                                ':' +
                                database_setting[ 'password' ] +
                                '@' +
                                database_setting[ 'host_name' ] +
                                '/' +
                                database_setting[ 'database_name' ]
                                )
                self.metadata = MetaData( self.engine )
                self.user_table = Table( 'user', self.metadata, 
                                autoload = True )
                
                # 将实体类User映射到user表
                mapper( User, self.user_table )

                # 生成一个会话类,并与上面建立的数据库引擎绑定
                self.Session = sessionmaker()
                self.Session.configure( bind = self.engine )
                
                # 创建一个会话
                self.session = self.Session()

        def CreateNewUser( self, user_info ):
                '''
                    # 这个方法根据传递过来的用户信息列表新建一个用户
                    # user_info是一个列表,包含了从表单提交上来的信息
                '''
                new_user = User( 
                                user_info[ 'user_name' ],
                                user_info[ 'user_age' ],
                                user_info[ 'user_sex' ],
                                user_info[ 'user_score' ],
                                user_info[ 'user_subject' ]
                                )
                self.session.add( new_user )                       # 增加新用户
                self.session.commit()                              # 保存修改

        def GetUserByName( self, user_name ):                      # 根据用户名返回信息
                return self.session.query( User ).filter_by( 
                                user_name = user_name ).all()[ 0 ]

        def GetAllUser( self ):                                    # 返回所有用户的列表
                return self.session.query( User )

        def UpdateUserInfoByName( self, user_info ):               # 根据提供的信息更新用户资料
                user_name = user_info[ 'user_name' ]
                user_info_without_name = { 'user_age':user_info[ 'user_age' ],
                                'user_sex':user_info[ 'user_sex' ],
                                'user_score':user_info[ 'user_score' ],
                                'user_subject':user_info[ 'user_subject' ]
                                }
                self.session.query( User ).filter_by( user_name = user_name ).update( 
                                user_info_without_name )
                self.session.commit()

        def DeleteUserByName( self, user_name ):                  # 删除指定用户名的用户
                user_need_to_delete = self.session.query( User ).filter_by( 
                                user_name = user_name ).all()[ 0 ]
                self.session.delete( user_need_to_delete )
                self.session.commit()
        上以代码非常经典,虽然没有进行异常捕捉等操作,但是这些代码展示了如何使用SQLAlchemy进行数据库操作的一般方法,增加、删除、修改都有了。关于SQLAlchemy更详细的信息可以参考官方文档。

四、tornado框架下的server主程序

        接下来就是本文最主要的代码了。将以下代码保存为server.py,并存储到项目目录下。

#!/usr/bin/env python

# This is a Web Server for UserManager

import tornado.httpserver                         # 引入tornado的一些模块文件
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options

import orm                                        # 引入刚刚编写的orm层代码

define( 'port', default = 9999, help = 'run on the given port', type = int )

user_orm = orm.UserManagerORM()                   # 创建一个全局ORM对象

class MainHandler( tornado.web.RequestHandler ):           # 主Handler,用来响应首页的URL
        '''
            MainHandler shows all data and a form to add new user
        '''
        def get( self ):                                   # 处理主页面(UserManager.html)的GET请求
                # show all data and a form
                title = 'User Manager V0.1'                # 这个title将会被发送到UserManager.html中的{{title}}部分
                
                users = user_orm.GetAllUser()              # 使用ORM获取所有用户的信息
                # 下面这一行会将title和users两个变量分别发送到指定模板的对应变量中去
                self.render( 'templates/UserManager.html', title = title, users = users )      # 并显示该模板页面
        
        def post( self ):
                pass                                       # 这里不处理POST请求

class AddUserHandler( tornado.web.RequestHandler ):        # 响应/AddUser的URL
        '''
            AddUserHandler collects info to create new user
        '''
        def get( self ):
                pass
        
        def post( self ):                                  # 这个URL只响应POST请求,用来收集用户信息并新建帐号
                # Collect info and create a user record in the database
                user_info = {
                                'user_name':self.get_argument( 'user_name' ),
                                'user_age':self.get_argument( 'user_age' ),
                                'user_sex':self.get_argument( 'user_sex' ),
                                'user_score':self.get_argument( 'user_score' ),
                                'user_subject':self.get_argument( 'user_subject' )
                                }
                user_orm.CreateNewUser( user_info )        # 调用ORM的方法将新建的用户信息写入数据库

                self.redirect( 'http://localhost:9999' )   # 页面转到首页

class EditUserHandler( tornado.web.RequestHandler ):       # 响应/EditUser的URL
        '''
            Show a page to edit user info,
            user name is given by GET method
        '''
        def get( self ):                                   # /EditUser的URL中,响应GET请求
                user_info = user_orm.GetUserByName( self.get_argument( 'user_name' ) )    # 利用ORM获取指定用户的信息
                self.render( 'templates/EditUserInfo.html', user_info = user_info )       # 将该用户信息发送到EditUserInfo.html以供修改

        def post( self ):
                pass

class UpdateUserInfoHandler( tornado.web.RequestHandler ):          # 用户信息编辑完毕后,将会提交到UpdateUserInfo,由此Handler处理
        '''
            Update user info by given list
        '''
        def get( self ):
                pass

        def post( self ):                                  # 调用ORM层的UpdateUserInfoByName方法来更新指定用户的信息
                user_orm.UpdateUserInfoByName({
                        'user_name':self.get_argument( 'user_name' ),
                        'user_age':self.get_argument( 'user_age' ),
                        'user_sex':self.get_argument( 'user_sex' ),
                        'user_score':self.get_argument( 'user_score' ),
                        'user_subject':self.get_argument( 'user_subject' ),
                        })
                self.redirect( 'http://localhost:9999' )   # 数据库更新后,转到首页

class DeleteUserHandler( tornado.web.RequestHandler ):     # 这个Handler用来响应/DeleteUser的URL
        '''
            Delete user by given name
        '''
        def get( self ):
                # 调用ORM层的方法,从数据库中删除指定的用户
                user_orm.DeleteUserByName( self.get_argument( 'user_name' ) )
                
                self.redirect( 'http://localhost:9999' )   # 数据库更新后,转到首页

        def post( self ):
                pass

def MainProcess():                                        # 主过程,程序的入口
        tornado.options.parse_command_line()
        application = tornado.web.Application( [          # 这里就是路由表,确定了哪些URL由哪些Handler响应
                ( r'/', MainHandler ),                    # 路由表中的URL是用正则表达式来过滤的
                ( r'/AddUser', AddUserHandler ),
                ( r'/EditUser', EditUserHandler ),
                ( r'/DeleteUser', DeleteUserHandler ),
                ( r'/UpdateUserInfo', UpdateUserInfoHandler ),
        ])

        http_server = tornado.httpserver.HTTPServer( application )
        http_server.listen( options.port )                # 在上面的的define中指定了端口为9999
        tornado.ioloop.IOLoop.instance().start()          # 启动服务器

if __name__ == '__main__':                                # 文件的入口
        MainProcess()
        这个server.py文件是一个典型的tornado服务器,不管多复杂的tornado应用,都是在上面这些代码的基础上开发的。


        以上四个文件就是本文例子的全部代码了,如果项目目录为UserManager,则server.py和orm.py都应当放在这个目录里面。该项目目录还包含有一个templates模板目录,UserManager.html和EditUserInfo.html两个HTML模板文件则在templates目录中。

        如果所有设置都没有问题,就可以在项目目录UserManager下执行 python server.py 命令来启动服务器了。启动后,在浏览器地址中输入 http://localhost:9999 就可以访问我们刚才建立的应用,你可以试着增加、删除用户,以及修改用户信息。

你可能感兴趣的:(Python)