搭建Tornado Https服务器之Tornado ORM模式(6)

       在经过前面的学习,我们已经学会使用pymysql对mysql数据库进行操作,但是随着数据库表的增多,字段增多,sql语句愈加的复杂和冗长,有没有什么工具可以使我们避免取直接写sql语句呢?这里还真有,就是ORM模式。本章节同时使用peewee模块实现ORM模式,peewee 虽说不是最好的但是是轻量级的。(实际上博主直接触了这个)

一、认识ORM

        ORM,全称Object Relational Mapping,中文意思是对象关系映射,是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式——百度百科,我的理解就是将我们数据库中的表以及一些约束和联系等等,以类的形式封装,每个表好比一个类,表的字段就是对象属性,当然实际上不是这么简单,我推荐一个 大佬的解释 阮一峰 ORM。使用ORM的好处就是 我们不再关心你使用什么样的数据库,对于不同的数据库的操作,使用同样代码 即可在不同数据库实现同样的功能,这里用的peewee支持很多的数据库。

二、将已有的数据库,转换成 model

       ok,话不多说,点到为止,如果大家有兴趣再深入研究,这里简单掠过,实际上博主也不是很懂,不敢造次,下面介绍如何将已有的数据库转换为py文件,也就是类文件,数据库模型,便于我们去调用类对象,从而对数据库进行操作。

1、如果没安装请安装peewee

pip install peewee 如果双环境 依据各人配置觉得 反正是python3环境嘛。同时奉上 peewee官网 

2、开始导出,生成数据库模型

使用的语句,这里我们不指定表,直接指定test数据库,全部生成一个文件model.py,当然你也可以指定具体的表

python3 -m pwiz -e mysql -H localhost -p 3306 -u root -P root test > model.py

奉上具体参数解释:有些参数是不必要用的

选项解释举例
-hhelp 显示帮助-h
-eengine 数据库引擎-e mysql
-Hhost 数据库地址-H localhost
-pport 数据库端口-p 3306
-uuser 数据库用户名-u root
-Ppassword 数据库密码-P root
-sschema 模式-s public
-ttables 指定生成来自表格-t users,classes
-vviews 指定生成来自视图-v
-iinfo 添加原信息-i
-oorder 保留表格列顺序-o

执行这个命令的时候我建议在数据库本机上允许,拉取要输入数据库密码的,注意不同操作系统获取的py文件编码格式不一样,容易报错误。

因为我的是云服务器,那我就从云服务器上拉取,同时如果没有安装python3 的环境和peewee、pymysql的包的请提前安装哦,毕竟Centos安装python3也不是件容易的事情,可以自行查阅也可以 查考 我的另一个 博客

ok,我们安装好之后,来拉取吧

可以看到,已经拉去成功啦,那我们把它放在我们工程目录下的handler目录下把吧。

 

可以打开model.py感受一下 ORM。对于这个文件重点说一下 连接数据库的连接属性,首先是参数 因为从数据库本机拉的,因此host为localhost 如果我们在自己的机器上使用云端的就得改成公网ip了;其次是对数据库操作的时候,peewee是有隐形连接的,但是为了防止数据库断开连接 我们自己主动去连接数据库和断开数据库。其中的类我们看英文就知道是什么意思了非常好懂,下面我们就开始改写我们之前的代码了。

from peewee import *

database = MySQLDatabase('test', **{'charset': 'utf8', 'sql_mode': 'PIPES_AS_CONCAT', 'use_unicode': True, 'host': 'localhost', 'port': 3306, 'user': 'root', 'password': 'root'})

class UnknownField(object):
    def __init__(self, *_, **__): pass

class BaseModel(Model):
    class Meta:
        database = database

# 对应了我们之前创建的classes表
class Classes(BaseModel):
    classname = CharField()
    createtime = DateTimeField(constraints=[SQL("DEFAULT CURRENT_TIMESTAMP")])
    id = BigAutoField()

    class Meta:
        table_name = 'classes'
# 对应了我们之前创建的users表
class Users(BaseModel):
    class_ = ForeignKeyField(column_name='class_id', field='id', model=Classes)
    createtime = DateTimeField(constraints=[SQL("DEFAULT CURRENT_TIMESTAMP")])
    id = BigAutoField()
    password = CharField(constraints=[SQL("DEFAULT '11111111'")])
    username = CharField()

    class Meta:
        table_name = 'users'

三、改写代码

在改写前,我们得在开发电脑上装上Python的依赖库peewee

1、改写userhandler.py文件作为例子,看看ORM模式下的增删改查,如果你还想留着原先的代码的话,建议备份一下

from basehandler import BaseHandler
# 从model中导出Users类 就是users表 以及数据库连接对象
from model import Users,database
# 这个模块能够将 model类型与字典互换 例如:users的一条记录 转换成 数据和字段一一对应的字典
# 这里我们只用到了 model_to_dict 字面的意思 model转换成字典
from playhouse.shortcuts import model_to_dict

# 这是一个处理新建用户的请求类,继承BaseHandler
class NewUserHandler(BaseHandler):
    # 重写post请求函数,处理post请求
    def post(self):
        # 为了防止出错导致程序崩溃,我们需要捕获异常函数,并处理 
        try:
            # 从请求中获取参数,如果获取不到,我们赋值 None
            username = self.get_argument('username', None)
            password = self.get_argument('password', None)
            class_id = self.get_argument('class_id', None)
            # 对传入的参数进行判断,如果为空我们回复 error,并终止执行下去
            if not username:
                # self.write()函数的参数为标准的json类型
                self.write({"error":"用户账号为空!"})
                return
            if not password:
                # self.write()函数的参数为标准的json类型
                self.write({"error":"用户密码为空!"})
                return
            if not class_id:
                # self.write()函数的参数为标准的json类型
                self.write({"error":"班级没有选择!"})
                return
            database.connect() # 连接数据库
            # 下面这句话的意思是根据 username获取用户信息 我们作做这一部是为了防止用户已存在
            # 如果用户不存在会抛出异常DoesNotExist 我们要捕获异常,否则程序报错
            user = Users.get(Users.username==username)
            self.write({"error":"用户已存在!"})
        except Users.DoesNotExist:
            # 下面的代码就是插入的操作,当然peewee不止一种插入方式,请学习peewee文档
            # 执行成功后会返回新插入的id
            user_id=Users.insert({
                'username':username,
                'password':password,
                'class_id':class_id
            }).execute()
            self.write({"success":"新建用户信息成功!"})
        except Exception as e:
            # 如果出现异常我们打印出来
            print(e)
            self.write({"error":"服务器出错!"})
        finally:
            # 不论是否出现异常,关闭数据库
            database.close()

# 这是一个处理获取用户信息的请求类,继承BaseHandler
class GetUserHandler(BaseHandler):
    # 重写get请求函数,处理get请求
    def get(self):
        # 为了防止出错导致程序崩溃,我们需要捕获异常函数,并处理 
        try:
            # 从请求中获取参数,如果获取不到,我们赋值 None
            user_id = self.get_argument('id', None)
            # 对传入的参数进行判断,如果为空我们回复 error,并终止执行下去
            if not user_id:
                # self.write()函数的参数为标准的json类型
                self.write({"error":"用户id为空!"})
                return
            database.connect() # 连接数据库
            user=Users.get_by_id(user_id) # 通过id得到用户信息
            user_dir=model_to_dict(user) # model转换成字典
            # 转换时间格式
            print(user_dir) #我们可以打印看看返回的是啥,这是必须的,不然你不知道他的数据结构,无法获取指定数据
            user_dir['createtime']=user.createtime.strftime('%Y-%m-%d %H:%M:%S')
            # 对于外键处理 它会自己把外键的相关信息封装一起,我们需要什么拿什么,在查询的时候无需联合查询
            user_dir['classname']=user.class_.classname
            del user_dir['class_'] # 将冗余的删除,只剩下一层json
            del user_dir['password'] 
            self.write({"success":user_dir})
        except Users.DoesNotExist:
            self.write({"error":"查无此人!"})
        except Exception as e:
            # 如果出现异常我们打印出来
            print(e)
            self.write({"error":"服务器出错!"})
        finally:
            database.close()

# 这是一个处理获取用户列表的请求类,继承BaseHandler
class ListUserHandler(BaseHandler):
    # 重写get请求函数,处理get请求
    def get(self):
        # 为了防止出错导致程序崩溃,我们需要捕获异常函数,并处理 
        try:
            database.connect()
            # select查询会返回多条记录,并且支持指定字段和条件查询以及排序
            users = Users.select().order_by(Users.createtime.asc())
            result_list=[]
            # 这个是返回查询条数的
            effect_row=users.count()
            # 这个是支持分页的,我们给的是一页10条数据,现在取首页
            users=users.paginate(1,10)
            for user in users:
                user_dir=model_to_dict(user)
                # 转换时间格式
                user_dir['createtime']=user.createtime.strftime('%Y-%m-%d %H:%M:%S')
                user_dir['classname']=user.class_.classname
                del user_dir['class_'] # 将冗余的删除,只剩下一层json
                del user_dir['password'] 
                result_list.append(user_dir)
            self.write({"success":result_list,"length":effect_row,"page":1,"limit":10})
        except Exception as e:
            print('user list post',e)
            self.write({"error":"服务器出错!"})
        finally:
            database.close()

# 这是一个处理更改用户信息的请求类,继承BaseHandler
# 这里我们只更改密码,如果你有想法的可以自己增加
class UpdateUserHandler(BaseHandler):
    # 重写get请求函数,处理get请求
    def post(self):
        # 为了防止出错导致程序崩溃,我们需要捕获异常函数,并处理 
        try:
            # 从请求中获取参数,如果获取不到,我们赋值 None
            user_id = self.get_argument('id', None)
            password = self.get_argument('password', None)
            # 对传入的参数进行判断,如果为空我们回复 error,并终止执行下去
            if not user_id:
                # self.write()函数的参数为标准的json类型
                self.write({"error":"用户账号为空!"})
                return
            if not password:
                # self.write()函数的参数为标准的json类型
                self.write({"error":"用户密码为空!"})
                return
            database.connect()
            # 下面代码是更新用户信息
            user=Users.get(Users.id==user_id) # 首先获取到用户
            # 其次更改用户字段
            user.password=password
            # 然后保存提交
            user.save()
            self.write({"success":"更新密码成功!"})
        except Users.DoesNotExist:
            self.write({"error":"用户密码错误!"})
        except Exception as e:
            # 如果出现异常我们打印出来
            print(e)
            self.write({"error":"服务器出错!"})
        finally:
            database.close()

# 这是一个处理删除用户信息的请求类,继承BaseHandler
class DeleteUserHandler(BaseHandler):
    # 重写get请求函数,处理get请求
    def get(self):
        # 为了防止出错导致程序崩溃,我们需要捕获异常函数,并处理 
        try:
            # 从请求中获取参数,如果获取不到,我们赋值 None
            user_id = self.get_argument('id', None)
            # 对传入的参数进行判断,如果为空我们回复 error,并终止执行下去
            if not user_id:
                # self.write()函数的参数为标准的json类型
                self.write({"error":"用户账号为空!"})
                return
            database.connect()
            # 下面代码是删除用户
            Users.delete().where(Users.id == user_id).execute()
            self.write({"success":"删除用户成功!!"})
        except Exception as e:
            print('user delete',e)
            self.write({"error":"服务器出错!"})
        finally:
            database.close()

2、其他文件代码无需动,当然如果你熟练的话是可以清理其他文件的

别忘记将之前的main.py文件中注释掉的关于user的路由解禁哦,否则会报404错误

import sys
# 将handler目录下的文件放到和main.py文件同级目录下,便于我们引用
sys.path.append("./handler")
# 添加必要的tornado的模块
import tornado.ioloop
import tornado.web
# 从classhandler.py文件中导出写好的 类
from classhandler import NewClassHandler
from userhandler import NewUserHandler,GetUserHandler,ListUserHandler,UpdateUserHandler,DeleteUserHandler
# 这是操作 postgresql 的路由
# from psqlhandler import NewUserHandler,GetUserHandler,ListUserHandler,UpdateUserHandler,DeleteUserHandler

# 写程序入口函数 main函数
def main():
    # 定义请求的路径和响应的请求类,此类会根据你发出的请求区分get 还是post而给予不同的处理
    application = tornado.web.Application([
        (r"/class/new", NewClassHandler),
        (r"/user/new", NewUserHandler),
        (r"/user/info", GetUserHandler),
        (r"/user/list", ListUserHandler),
        (r"/user/update", UpdateUserHandler),
        (r"/user/delete", DeleteUserHandler),
        # (r"/psql/new", NewUserHandler),
        # (r"/psql/info", GetUserHandler),
        # (r"/psql/list", ListUserHandler),
        # (r"/psql/update", UpdateUserHandler),
        # (r"/psql/delete", DeleteUserHandler),
        ])
    # 绑定端口,单进程启动
    application.listen(8000)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()

四、现在我们来测试一下吧

1、建用户

相同的信息再建一次,看看如何 

 

2、查看信息

看一下peewee返回的数据结构 ,class是不是整个信息都返回了呢?

 3、查看列表

4、更新信息

5、删除用户 

五、总结 

    OK,到此为止,是否发现 我们在心得userhandler.py文件中就没有涉及到pymysql驱动,也没有sql语句,由此看来ORM在实际使用中不关心你数据库是啥,它们都已经封装好了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江湖人称王某人的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值