前面的兩篇文章中,咱們已經學習了經過Flask開發GET和POST請求接口,但一直沒有實現操做數據庫,那麼咱們今天的目的,就是學習如何將MySQL數據庫運用到當前的接口項目中。html
本人環境:Python 3.7.0 、MySQL 5.7python
通常狀況,Flask操做MySQL比較常見的方式有2種:SQLAlchemy操做
和 SQL操做
。經過SQLAlchemy操做時,由於大可能是經過數據庫對象來操做,因此不須要寫多少SQL語句,但爲了順便鞏固一下SQL知識,在本文章中,咱們將採用SQL操做來進行學習。mysql
目前網上的相關文章中,大多數介紹的都是SQLAlchemy操做,SQLAlchemy操做相對也更簡單好用一些,由於SQLAlchemy爲Python與SQL之間創建了映射,若是使用它能夠極大減小SQL語句的編寫。git
在使用SQL操做方式時,咱們須要知道如何利用Python操做MySQL數據庫,若是不太清楚的話,能夠參考我以前的一篇文章:利用Python操做MySQL數據庫github
咱們建立一個數據庫,命名爲 flask_demo
,而後新建一個數據表 user
,建表語句以下:sql
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
來查看下錶結構。json
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 的服務器地址、端口、用戶名及密碼、數據庫名等參數。flask
# 服務端口配置 SERVER_PORT = 9999 # MySQL配置 MYSQL_HOST = "192.168.89.128" MYSQL_PORT = 3306 MYSQL_USER = "root" MYSQL_PASSWD = "123456" MYSQL_DB = "flask_demo"
咱們在項目根路徑下新建一個包 common
,在該包下新建文件 mysql_operate.py
,該文件下封裝了Python操做MySQL的代碼,後續將經過調用該文件的 db
對象及方法來操做數據庫。api
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)
@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": "查詢成功"})
@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": "查不到相關用戶的信息"})
@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": "上海市黃浦區"}
@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
在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 &
OK,經過以上內容,咱們已經成功將MySQL引入到咱們的接口項目中,並能夠成功進行接口請求,相關代碼已上傳到GitHub,你們有興趣的能夠基於此進行簡單部署及開展接口測試。
GitHub源碼地址:https://github.com/wintests/flaskDemo