美文网首页
使用Flask开发简单接口(3)--引入MySQL

使用Flask开发简单接口(3)--引入MySQL

作者: wintests | 来源:发表于2020-04-21 00:35 被阅读0次

    前言

    前面的两篇文章中,我们已经学习了通过Flask开发GET和POST请求接口,但一直没有实现操作数据库,那么我们今天的目的,就是学习如何将MySQL数据库运用到当前的接口项目中。

    本人环境:Python 3.7.0MySQL 5.7

    Flask操作MySQL的2种方式

    一般情况,Flask操作MySQL比较常见的方式有2种:SQLAlchemy操作SQL操作 。通过SQLAlchemy操作时,因为大多是通过数据库对象来操作,所以不需要写多少SQL语句,但为了顺便巩固一下SQL知识,在本文章中,我们将采用SQL操作来进行学习。

    目前网上的相关文章中,大多数介绍的都是SQLAlchemy操作,SQLAlchemy操作相对也更简单好用一些,因为SQLAlchemy为Python与SQL之间建立了映射,如果使用它可以极大减少SQL语句的编写。

    在使用SQL操作方式时,我们需要知道如何利用Python操作MySQL数据库,如果不太清楚的话,可以参考我之前的一篇文章:利用Python操作MySQL数据库

    数据库设计

    我们创建一个数据库,命名为 flask_demo ,然后新建一个数据表 user ,建表语句如下:

    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `username` varchar(20) NOT NULL,
      `password` varchar(255) NOT NULL,
      `role` tinyint(1) NOT NULL,
      `sex` tinyint(1) DEFAULT NULL,
      `telephone` varchar(255) NOT NULL,
      `address` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `telephone` (`telephone`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    user表中各字段对应含义如下:

    id:用户id号,自增长
    username:用户名
    password:密码
    role:用户角色,0表示管理员用户,1表示普通用户
    sex:性别,0表示男性,1表示女性,允许为空
    telephone:手机号
    address:联系地址,允许为空
    

    user表创建完成后,通过 DESC user 来查看下表结构。

    mysql> DESC user;
    +-----------+--------------+------+-----+---------+----------------+
    | Field     | Type         | Null | Key | Default | Extra          |
    +-----------+--------------+------+-----+---------+----------------+
    | id        | int(11)      | NO   | PRI | NULL    | auto_increment |
    | username  | varchar(20)  | NO   |     | NULL    |                |
    | password  | varchar(255) | NO   |     | NULL    |                |
    | role      | tinyint(1)   | NO   |     | NULL    |                |
    | sex       | tinyint(1)   | YES  |     | NULL    |                |
    | telephone | varchar(255) | NO   | UNI | NULL    |                |
    | address   | varchar(255) | YES  |     | NULL    |                |
    +-----------+--------------+------+-----+---------+----------------+
    7 rows in set (0.00 sec)
    

    新增配置文件

    我们在项目根路径下新建一个包 config ,在该包下存放配置文件 setting.py ,该文件用于配置 MySQL 的服务器地址、端口、用户名及密码、数据库名等参数。

    # 服务端口配置
    SERVER_PORT = 9999
    
    # MySQL配置
    MYSQL_HOST = "192.168.89.128"
    MYSQL_PORT = 3306
    MYSQL_USER = "root"
    MYSQL_PASSWD = "123456"
    MYSQL_DB = "flask_demo"
    

    Python操作MySQL

    我们在项目根路径下新建一个包 common ,在该包下新建文件 mysql_operate.py ,该文件下封装了Python操作MySQL的代码,后续将通过调用该文件的 db 对象及方法来操作数据库。

    import pymysql
    from config.setting import MYSQL_HOST,MYSQL_PORT,MYSQL_USER,MYSQL_PASSWD,MYSQL_DB
    
    class MysqlDb():
    
        def __init__(self, host, port, user, passwd, db):
            # 建立数据库连接
            self.conn = pymysql.connect(
                host=host,
                port=port,
                user=user,
                passwd=passwd,
                db=db
            )
            # 通过 cursor() 创建游标对象,并让查询结果以字典格式输出
            self.cur = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
    
        def __del__(self): # 对象资源被释放时触发,在对象即将被删除时的最后操作
            # 关闭游标
            self.cur.close()
            # 关闭数据库连接
            self.conn.close()
    
        def select_db(self, sql):
            """查询"""
            # 检查连接是否断开,如果断开就进行重连
            self.conn.ping(reconnect=True)
            # 使用 execute() 执行sql
            self.cur.execute(sql)
            # 使用 fetchall() 获取查询结果
            data = self.cur.fetchall()
            return data
    
        def execute_db(self, sql):
            """更新/新增/删除"""
            try:
                # 检查连接是否断开,如果断开就进行重连
                self.conn.ping(reconnect=True)
                # 使用 execute() 执行sql
                self.cur.execute(sql)
                # 提交事务
                self.conn.commit()
            except Exception as e:
                print("操作出现错误:{}".format(e))
                # 回滚所有更改
                self.conn.rollback()
    
    db = MysqlDb(MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASSWD, MYSQL_DB)
    

    完善GET和POST请求接口

    • 获取所有用户信息(GET接口)
    @app.route("/users", methods=["GET"])
    def get_all_users():
        """获取所有用户信息"""
        sql = "SELECT * FROM user"
        data = db.select_db(sql)
        print("获取所有用户信息 == >> {}".format(data))
        return jsonify({"code": "0", "data": data, "msg": "查询成功"})
    
    • 获取某个用户信息(GET接口)
    @app.route("/users/<string:username>", methods=["GET"])
    def get_user(username):
        """获取某个用户信息"""
        sql = "SELECT * FROM user WHERE username = '{}'".format(username)
        data = db.select_db(sql)
        print("获取 {} 用户信息 == >> {}".format(username, data))
        if data:
            return jsonify({"code": "0", "data": data, "msg": "查询成功"})
        return jsonify({"code": "1004", "msg": "查不到相关用户的信息"})
    
    • 用户注册接口(POST接口)
    @app.route("/register", methods=['POST'])
    def user_register():
        """用户注册"""
        username = request.json.get("username").strip()  # 用户名
        password = request.json.get("password").strip()  # 密码
        sex = request.json.get("sex", "0").strip()  # 性别,默认为0(男性)
        telephone = request.json.get("telephone", "").strip()  # 手机号,默认为空串
        address = request.json.get("address", "").strip()  # 地址,默认为空串
        if username and password and telephone:
            sql1 = "SELECT username FROM user WHERE username = '{}'".format(username)
            res1 = db.select_db(sql1)
            print("查询到用户名 ==>> {}".format(res1))
            sql2 = "SELECT telephone FROM user WHERE telephone = '{}'".format(telephone)
            res2 = db.select_db(sql2)
            print("查询到手机号 ==>> {}".format(res2))
            if res1:
                return jsonify({"code": 2002, "msg": "用户名已存在,注册失败!!!"})
            elif not (sex == "0" or sex == "1"):
                return jsonify({"code": 2003, "msg": "输入的性别只能是 0(男) 或 1(女)!!!"})
            elif not (len(telephone) == 11 and re.match("^1[3,5,7,8]\d{9}$", telephone)):
                return jsonify({"code": 2004, "msg": "手机号格式不正确!!!"})
            elif res2:
                return jsonify({"code": 2005, "msg": "手机号已被注册!!!"})
            else:
                sql3 = "INSERT INTO user(username, password, role, sex, telephone, address) " \
                      "VALUES('{}', '{}', '1', '{}', '{}', '{}')".format(username, password, sex, telephone, address)
                db.execute_db(sql3)
                print("新增用户信息 ==>> {}".format(sql3))
                return jsonify({"code": 0, "msg": "恭喜,注册成功!"})
        else:
            return jsonify({"code": 2001, "msg": "用户名/密码/手机号不能为空,请检查!!!"})
    

    在这里,我们实现用户注册的时候,设置只能注册 role 字段值为 1 的普通用户,不允许直接注册管理员用户。相关的接口返回码和请求场景如下:

    接口返回码 请求场景
    0 请求参数正确,注册成功
    2001 请求参数中,用户名/密码/手机号,任一参数为空
    2002 请求参数中,用户名已被其他人注册使用
    2003 请求参数中, sex 性别字段值不是 0 或 1
    2004 请求参数中,手机号格式不正确
    2005 请求参数中,手机号已被其他人注册使用

    可参考如下进行用户注册接口请求:

    请求方式:POST
    请求地址:http://127.0.0.1:5000/register
    请求头:
    Content-Type: application/json
    
    Body:{"username": "wintest5", "password": "123456", "sex": "1", "telephone":"13500010005", "address": "上海市黄浦区"}
    
    接口请求示例
    • 用户登录接口(POST接口)
    @app.route("/login", methods=['POST'])
    def user_login():
        """用户登录"""
        username = request.values.get("username").strip()
        password = request.values.get("password").strip()
        if username and password:
            sql1 = "SELECT username FROM user WHERE username = '{}'".format(username)
            res1 = db.select_db(sql1)
            print("查询到用户名 ==>> {}".format(res1))
            if not res1:
                return jsonify({"code": 1003, "msg": "用户名不存在!!!"})
            sql2 = "SELECT * FROM user WHERE username = '{}' and password = '{}'".format(username, password)
            res2 = db.select_db(sql2)
            print("获取 {} 用户信息 == >> {}".format(username, res2))
            if res2:
                return jsonify({"code": 0, "msg": "恭喜,登录成功!"})
            return jsonify({"code": 1002, "msg": "用户名或密码错误!!!"})
        else:
            return jsonify({"code": 1001, "msg": "用户名或密码不能为空!!!"})
    

    相关的接口返回码和请求场景如下:

    接口返回码 请求场景
    0 用户名和密码正确,登录成功
    1001 请求参数中,用户名或密码为空
    1002 请求参数中,用户名正确,密码错误
    1003 请求参数中,使用了未注册的用户名

    可参考如下进行用户登录接口请求:

    请求方式:POST
    请求地址:http://127.0.0.1:5000/login
    请求头:
    Content-Type: application/x-www-form-urlencoded
    
    Body:username=wintest&password=123456
    
    接口请求示例

    将项目在服务器部署

    • app.run() 启动应用时配置参数

    在Flask中,我们通过 app.run() 启动应用时,可以配置一些参数,比如可以配置如下:

    from config.setting import SERVER_PORT
    if __name__ == '__main__':
        # host为主机ip地址,port指定访问端口号,debug=True设置调试模式打开
        app.run(host="0.0.0.0", port=SERVER_PORT, debug=True)
    

    上面代码中,host需设置为 0.0.0.0,这样才能在外网进行访问,比如我们在Linux下部署项目,如果想在Windows中进行请求访问,那么就需要设置为 0.0.0.0

    • 将项目根路径加入环境变量

    我们把项目根路径下的 app.py 当做该项目的应用启动入口文件,那么就需要将项目的根路径临时加入到环境变量中,否则启动应用时可能会提示找不到相关模块,具体配置如下:

    import os, sys
    from config.setting import SERVER_PORT
    from api.user import app
    
    # 项目根路径
    BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.insert(0, BASE_PATH)  # 将项目根路径临时加入环境变量,程序退出后失效
    
    if __name__ == '__main__':
        # host为主机ip地址,port指定访问端口号,debug=True设置调试模式打开
        app.run(host="0.0.0.0", port=SERVER_PORT, debug=True)
    
    • 命令行启动应用

    我们这个项目的启动,只需要一行命令就可以搞定,以Linux下后台运行方式启动为例,命令如下:

    # /root/flaskDemo/app.py表示项目根路径下的app.py启动入口文件路径
    # /root/flaskDemo/flaskDemo.log表示输出的日志文件路径
    nohup python3 /root/flaskDemo/app.py >/root/flaskDemo/flaskDemo.log 2>&1 &
    
    Linux下启动项目

    OK,通过以上内容,我们已经成功将MySQL引入到我们的接口项目中,并可以成功进行接口请求,相关代码已上传到GitHub,大家有兴趣的可以基于此进行简单部署及开展接口测试。

    GitHub源码地址:https://github.com/wintests/flaskDemo

    相关文章

      网友评论

          本文标题:使用Flask开发简单接口(3)--引入MySQL

          本文链接:https://www.haomeiwen.com/subject/kepjvhtx.html