在Flask生态里操作MySQL,最常见的问题就是该选哪种方式。有人直接用pymysql写原生SQL,觉得简单可控;有人用flask-sqlalchemy做ORM,开发速度快但心里没底。这两种方案没有绝对的好坏,关键在于理解它们各自的工作机制和适用边界。本文会把两条路线的配置、写法、坑点都梳理一遍,帮你做出合适的选择。

一、先搞清楚Flask连接MySQL的依赖关系
Flask本身是一个轻量Web框架,它不内置数据库支持,需要自己组合依赖。如果用ORM方式,通常需要两个包:SQLAlchemy负责数据库抽象层,flask-sqlalchemy负责把SQLAlchemy和Flask的应用上下文整合起来,PyMySQL则是Python与MySQL通信的驱动。也就是说,即便你用的是ORM,底层真正建立TCP连接、传输SQL语句的仍然是PyMySQL这类驱动。
安装方式如下:
pip install flask flask-sqlalchemy pymysql
这里有个新手常踩的坑:如果只装了SQLAlchemy而没装PyMySQL,连接MySQL时会报ModuleNotFoundError或者Can't load plugin之类的错误。另外MySQL 8.0默认使用caching_sha2_password认证方式,老版本驱动可能连不上,PyMySQL在较新版本中已经支持,遇到问题可以先升级驱动再排查其他原因。
连接字符串的写法也值得注意,正确格式是mysql+pymysql://用户名:密码@主机:3306/数据库名?charset=utf8mb4。前半段的mysql+pymysql告诉SQLAlchemy使用PyMySQL作为DBAPI,冒号和@符号如果出现在密码里,必须做URL编码,否则解析会出错,这也是很多人配置看起来没问题却连不上的原因。
二、flask-sqlalchemy的模型定义与增删改查
flask-sqlalchemy是Flask官方推荐的ORM整合方案,它的核心价值在于把数据库表映射成Python类,让你用操作对象的方式操作数据。先看一个完整的最小示例:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# 配置数据库连接
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/test?charset=utf8mb4'
# 关闭对模型修改的追踪,节省内存
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(50), nullable=False)
email = db.Column(db.String(100), unique=True)
def to_dict(self):
return {'id': self.id, 'name': self.name, 'email': self.email}
@app.route('/users')
def list_users():
users = User.query.all()
return {'data': [u.to_dict() for u in users]}
if __name__ == '__main__':
with app.app_context():
db.create_all() # 根据模型自动建表
app.run(debug=True)增删改查的基本写法如下。插入数据要经历三步:db.session.add()把对象放入会话,db.session.commit()真正提交事务,出错时用db.session.rollback()回滚。这个session不是浏览器的session,它是SQLAlchemy的工作单元,负责追踪对象变化并把它翻译成SQL。
# 插入 user = User(name='张三', email='zhangsan@ipipp.com') db.session.add(user) db.session.commit() # 查询 user = User.query.filter_by(name='张三').first() total = User.query.count() # 更新 user.email = 'new@ipipp.com' db.session.commit() # 删除 db.session.delete(user) db.session.commit()
需要注意flask-sqlalchemy 3.x和2.x的API有差异,3.x更推荐用db.session.execute(db.select(User))替代旧的User.query风格,旧写法虽然还能用,但新项目建议跟进新API,避免日后升级踩坑。
三、PyMySQL原生操作:更轻但责任更大
如果你觉得ORM太重,直接用PyMySQL也完全可行。它的优点是SQL完全由你掌控,执行路径清晰,排查问题直观;缺点是手写SQL容易拼错、需要自己管理连接和事务、没有对象映射能力。一个典型的写法是封装一个数据库工具类:
import pymysql
from flask import current_app
def get_conn():
return pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123456',
database='test',
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor # 查询结果返回字典
)
def query_users(name):
conn = get_conn()
try:
with conn.cursor() as cursor:
sql = 'SELECT id, name, email FROM users WHERE name = %s'
cursor.execute(sql, (name,)) # 参数化查询,防止SQL注入
return cursor.fetchall()
finally:
conn.close()
def add_user(name, email):
conn = get_conn()
try:
with conn.cursor() as cursor:
sql = 'INSERT INTO users (name, email) VALUES (%s, %s)'
cursor.execute(sql, (name, email))
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()这里有三点必须强调。第一,永远不要用字符串拼接构造SQL,%s占位符配合参数传递是防止SQL注入的底线,任何把用户输入直接拼进SQL的做法都是安全隐患。第二,原生的conn.cursor()默认返回元组,加上DictCursor后返回字典,取字段会方便很多。第三,每次请求都新建连接开销不小,小型项目无所谓,并发上来后应该引入连接池,比如用DBUtils的PooledDB,或者干脆回到SQLAlchemy,它自带连接池管理。
四、两种方案怎么选:从项目规模和团队能力出发
选型的判断依据其实很实际。如果你在做中大型项目、表结构会随业务演化、团队成员水平参差不齐,flask-sqlalchemy的价值会越来越明显:模型即文档、自动处理事务、防止注入、迁移工具Alembic配合管理表结构变更,这些能力在长期维护中省下的时间远超学习成本。
反之,如果项目很小、查询高度定制化(复杂报表、大量多表关联统计)、或者团队对SQL非常熟,原生PyMySQL反而更直接。还有一种折中做法:整体用ORM,个别性能敏感的查询用db.session.execute()执行原生SQL,两种方式共存并不冲突:
from sqlalchemy import text
# 在ORM项目中执行原生SQL
rows = db.session.execute(
text('SELECT name, COUNT(*) AS cnt FROM users GROUP BY name HAVING cnt > :n'),
{'n': 5}
).fetchall()最后提醒两个通用配置点。一是连接池参数,SQLAlchemy可通过SQLALCHEMY_ENGINE_OPTIONS设置pool_size和pool_recycle,后者建议设为3600左右,避免连接被MySQL的wait_timeout掐断后报MySQL server has gone away。二是字符集统一用utf8mb4,如果只在连接串里设置而建表时用了utf8,遇到emoji或生僻字依然会报错。把这些细节提前处理好,数据库层就不会成为项目的短板。
Flask MySQLflask-sqlalchemypymysql修改时间:2026-09-15 14:24:42