跳到主要内容

Flask-SQLAlchemy 的基本使用

安装

python -m pip install flask
python -m pip install flask-sqlalchemy
python -m pip install pymysql

建议直接安装当前稳定版(本笔记基准:Flask 3.1.3 + Flask-SQLAlchemy 3.x)。

连接数据库

连接mysql数据库

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text

app = Flask(__name__)

DB_HOST = "192.168.1.112"
DB_PORT = 3306
DB_USER = "root"
DB_PASS = "123456"
DB_NAME = "flask"

app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}"

db = SQLAlchemy(app)

# 低版本flask不需要写 app_context
with app.app_context():
with db.engine.connect() as conn:
# 低版本sqlalchemy不需要 text
rs = conn.execute(text("select 1"))
print(rs.fetchone())

运行输出结果

(1,)

ORM模型

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text

app = Flask(__name__)

DB_HOST = "192.168.1.112"
DB_PORT = 3306
DB_USER = "root"
DB_PASS = "123456"
DB_NAME = "flask"

app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}"

db = SQLAlchemy(app)

class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(80), nullable=False)
password = db.Column(db.String(80), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)

def __init__(self, username, password, email):
self.username = username
self.password = password
self.email = email

def __repr__(self) -> str:
return f"<User {self.id}>"

with app.app_context():
# 将User Model映射为数据表
db.create_all()

Flask-SQLAlchemy还支持以下字段类型

类型说明
db.Integer整型
db.SmallInteger短整型
db.BigInteger长整型
db.Decimal定点类型,可以指定小数位
db.Boolean布尔类型
db.Date日期类型。存储datetime.date对象
db.DateTime日期时间类型。存储datetime.datetime对象
db.Time时间类型。存储Python的datetime.time对象
db.Interval时间间隔。存储Python的datetime.timedelay对象
db.String字符串类型
db.Text文本类型
db.Enum枚举类型
db.PickleType存储经过pickle的对象
db.LargeBinary存储二进制数据

CRUD操作

一个简单的用户增删改查服务

from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
import json
# from sqlalchemy import text

app = Flask(__name__)

DB_HOST = "192.168.1.112"
DB_PORT = 3306
DB_USER = "root"
DB_PASS = "123456"
DB_NAME = "flask"

app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}"

db = SQLAlchemy(app)

class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(80), nullable=False)
password = db.Column(db.String(80), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)

def __init__(self, username:str, password:str, email:str):
self.username = username
self.password = password
self.email = email

@app.get("/")
def root():
return "Hello World"

@app.post("/moduser")
def mod_user():
# 根据查询参数获取 user id
userId = request.args.get('userId')
if userId is None:
return jsonify({
"code": 400,
"msg": "incomplete arguments",
})

req_body = request.get_data()
req_body = json.loads(req_body)
if req_body is None:
return jsonify({
"code": 400,
"msg": "incomplete arguments",
})

user = User.query.filter_by(id=userId).first()
if user is None:
return jsonify({
"code": 400,
"msg": "user not found",
})

user.username = req_body.get('username')
db.session.commit()
resp = {
"code": 200,
"msg": "success",
}
return jsonify(resp)

@app.post('/deluser')
def del_user():
userId = request.args.get('userId')
if userId is None:
return jsonify({
"code": 400,
"msg": "incomplete arguments",
})
user = User.query.filter_by(id=userId).first()
if user is None:
return jsonify({
"code": 400,
"msg": "user not found",
})
else:
db.session.delete(user)
db.session.commit()
resp = {
"code": 200,
"msg": "success",
}
return jsonify(resp)

@app.post('/adduser')
def add_user():
req_data = request.get_data()
req_data = json.loads(req_data)
req_username = req_data.get('username')
req_password = req_data.get('password')
req_email = req_data.get('email')
if not req_username or not req_password or not req_email:
return jsonify({
"code": 400,
"msg": "incomplete arguments",
})

select_email = User.query.filter_by(email = req_email).first()
if select_email is not None:
return jsonify({
"code": 400,
"msg": "The email address has been registered",
})

user = User(username=req_username, password=req_password, email=req_email)
db.session.add(user)
db.session.commit()

rst = User.query.filter_by(email=req_email).first()

resp = {
"code": 200,
"msg": "success",
"data": {
"userId": rst.id,
}
}
# print(resp)
return jsonify(resp)

# 查询用户
@app.post("/user")
def get_user():
userId = request.args.get('userId')
if userId is None:
return jsonify({
"code": 400,
"msg": "incomplete arguments",
})
user = User.query.filter_by(id=userId).first()
if user is None:
return jsonify({
"code": 400,
"msg": "user not found",
})
else:
resp = {
"code": 200,
"msg": "success",
"data": {
"username": user.username,
}
}
return jsonify(resp)

if __name__ == '__main__':
app.run(host="127.0.0.1",port=5000,debug=False)

使用pytest测试

import requests
import json
import pytest

headers = {
"Content-Type": "application/json"
}


def test_moduser():
url = "http://127.0.0.1:5000/moduser?userId=13"
payload = {
"username": "lisi"
}
resp = requests.post(url,data=json.dumps(payload),headers=headers)
print(resp.json())
assert resp.json()["code"] == 200

def test_deluser():
url = "http://127.0.0.1:5000/deluser?userId=13"
resp = requests.post(url,headers=headers)
print(resp.json())
assert resp.json()["code"] == 200

def test_adduser():
url = "http://127.0.0.1:5000/adduser"

payload = {
"username": "test",
"password": "123456",
"email": "test15@demo.com"
}
resp = requests.post(url,data=json.dumps(payload),headers=headers)
print(resp.json())
assert resp.json()["code"] == 200

def test_getuser():
url = "http://127.0.0.1:5000/user?userId=14"
resp = requests.post(url,headers=headers)
print(resp.json())
assert resp.json()["code"] == 200

if __name__ == "__main__":
pytest.main(["-v","-s","test_demo.py"])

增加数据

增加数据需要创建对象,创建对象时必须通过关键字给字段赋值,尤其是那些Not Null的字段

# 创建对象
user1 = User(username="zhangda",password="111")
user2 = User(username="zhanger",password="222")
user3 = User(username="zhangsan",password="333")

# 可以先把对象都添加到session中再统一commit
db.session.add(user1)
db.session.add(user2)
db.session.add(user3)

db.session.commit()

删除数据

删除单条数据(SQLAlchemy 2.0 推荐 db.session.get

user = db.session.get(User, 1)
db.session.delete(user)
db.session.commit()

删除多条数据

User.query.filter(User.username.contains("zhangsan")).delete(synchronize_session=False)
db.session.commit()

更新数据

修改一条数据

user = db.session.get(User, 1)
user.username = "zhangsi"
db.session.commit()

修改多条数据

User.query.filter(User.username.like("%zhangsan%")).update({"password": "qazxsw"}, synchronize_session=False)
db.session.commit()

查询数据

# 获取查询结果集中的所有对象, 是列表类型
query.all()

# 查询结果集的第一个对象
query.first()

# 获取查询结果集中的一个对象
query.one()

# 类似query.one(), 不过对象数量不等于1时不抛出异常
query.one_or_none()

# 根据主键获取数据(SQLAlchemy 2.0 推荐写法,旧版 query.get 已移除)
db.session.get(User, pk)

# 判断数据是否存在
db.session.query(User).filter(User.username == "zhangsan").exists()

# 获取结果集的个数
query.count()

# 根据查询条件过滤
users = User.query.filter(User.username=="zhangsan").all()

# 根据关键字参数过滤
users = User.query.filter_by(username="zhangsan").all()

# 对结果进行切片操作
query.slice(start,stop)

# limit限制
query.limit(limit)

# offset
query.offset(offset)

# 排序
query.order_by()

# 分组
query.group_by()

# like模糊查询
users = User.query.filter(User.username.like("%zhangsan%"))

# in
users =User.query.filter(User.username.in_(["zhangsan","lisi","wangwu"]))

# not in
users = User.query.filter(~User.username.in_(["zhangsan","lisi"]))

# is null
users = User.query.filter(User.username==None)

# is not null
users = User.query.filter(User.username != None)

# and
from sqlalchemy import and_
users = User.query.filter(and_(User.username == "zhangsan", User.email.like("%@demo.com")))

# or
from sqlalchemy import or_
users = User.query.filter(or_(User.username == "zhangsan", User.email == "lisi@demo.com"))