1. Flask与MySQL数据库连接实战
在Web开发中,数据库操作是后端开发的核心环节。作为Python轻量级框架的Flask,与MySQL这一关系型数据库的结合,是中小型项目的经典技术栈选择。我将在本文中分享如何高效实现Flask与MySQL的对接,并通过ORM进行数据操作。
提示:本文基于Python 3.8+和Flask 2.0+环境,MySQL版本建议使用5.7或8.0
1.1 环境准备与依赖安装
首先需要安装必要的Python包:
bash复制pip install flask flask-sqlalchemy pymysql cryptography
这里选择了四个关键组件:
flask: 基础框架flask-sqlalchemy: Flask的ORM扩展pymysql: MySQL的Python驱动cryptography: 用于安全连接
为什么选择SQLAlchemy而不是直接使用原生SQL?主要基于三点考虑:
- 安全性:自动处理SQL注入问题
- 可移植性:支持多种数据库后端
- 开发效率:面向对象的方式更符合Python哲学
1.2 数据库配置
在Flask应用中进行数据库配置时,推荐使用类配置方式:
python复制class Config:
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://username:password@host:port/database'
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = True # 开发时开启,生产环境务必关闭
配置参数说明:
username/password: 数据库认证信息host:port: 数据库服务器地址,本地开发常用127.0.0.1:3306database: 要连接的数据库名称
重要:生产环境务必使用环境变量存储敏感信息,不要硬编码在代码中
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. ORM模型定义与关系映射
2.1 基础模型定义
SQLAlchemy的模型定义非常直观,以用户模型为例:
python复制from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return f'<User {self.username}>'
字段类型选择建议:
- 字符串:String(length)
- 文本内容:Text
- 整数:Integer/BigInteger
- 浮点数:Float/Numeric
- 布尔值:Boolean
- 日期时间:DateTime/Date/Time
2.2 模型关系定义
关系型数据库的核心在于关系,SQLAlchemy支持四种主要关系:
- 一对一关系:
python复制class Profile(db.Model):
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
user = db.relationship('User', backref=db.backref('profile', uselist=False))
- 一对多关系(以博客文章为例):
python复制class Post(db.Model):
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
author = db.relationship('User', backref='posts')
- 多对多关系(需要关联表):
python复制tags = db.Table('post_tags',
db.Column('post_id', db.Integer, db.ForeignKey('post.id')),
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
)
class Post(db.Model):
tags = db.relationship('Tag', secondary=tags, backref='posts')
3. CRUD操作全解析
3.1 创建(Create)操作
基本创建操作:
python复制new_user = User(username='john', email='john@example.com')
db.session.add(new_user)
db.session.commit()
批量创建更高效:
python复制users = [User(username=f'user{i}', email=f'user{i}@test.com') for i in range(10)]
db.session.add_all(users)
db.session.commit()
注意:务必在修改操作后调用commit(),否则更改不会持久化
3.2 读取(Read)操作
基础查询:
python复制# 获取所有用户
users = User.query.all()
# 获取单个用户
user = User.query.get(1) # 通过主键获取
user = User.query.filter_by(username='john').first()
复杂查询:
python复制# 分页查询
page = User.query.order_by(User.created_at.desc()).paginate(page=1, per_page=10)
# 多条件查询
from sqlalchemy import and_, or_
users = User.query.filter(
or_(
User.username.like('%john%'),
and_(
User.created_at > datetime(2023,1,1),
User.email.endswith('@example.com')
)
)
).all()
3.3 更新(Update)操作
直接修改属性后提交:
python复制user = User.query.get(1)
user.email = 'new_email@example.com'
db.session.commit()
批量更新更高效:
python复制User.query.filter_by(active=False).update({'active': True})
db.session.commit()
3.4 删除(Delete)操作
单个删除:
python复制user = User.query.get(1)
db.session.delete(user)
db.session.commit()
批量删除:
python复制User.query.filter_by(active=False).delete()
db.session.commit()
4. 高级技巧与性能优化
4.1 连接池配置
生产环境中,连接池配置至关重要:
python复制app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
'pool_size': 10,
'max_overflow': 20,
'pool_recycle': 3600,
'pool_pre_ping': True
}
参数说明:
pool_size: 保持的连接数max_overflow: 允许超出的连接数pool_recycle: 连接回收时间(秒)pool_pre_ping: 自动检测连接有效性
4.2 事务管理
复杂操作应该放在事务中:
python复制try:
db.session.begin_nested()
# 执行多个操作
db.session.commit()
except Exception as e:
db.session.rollback()
raise e
4.3 查询优化技巧
- 只查询需要的字段:
python复制users = db.session.query(User.username, User.email).all()
- 使用join优化关联查询:
python复制result = db.session.query(User, Post).join(Post, User.id == Post.author_id).all()
- 避免N+1查询问题:
python复制# 不好的方式:会导致N+1查询
users = User.query.all()
for user in users:
print(user.posts) # 每次循环都会查询数据库
# 好的方式:使用joinedload
from sqlalchemy.orm import joinedload
users = User.query.options(joinedload(User.posts)).all()
5. 常见问题与解决方案
5.1 连接超时问题
错误现象:
code复制OperationalError: (pymysql.err.OperationalError) (2013, 'Lost connection to MySQL server during query')
解决方案:
- 增加连接超时时间:
python复制app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
'connect_args': {'connect_timeout': 10}
}
- 启用连接池预检测:
python复制app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {'pool_pre_ping': True}
5.2 编码问题
确保数据库、连接和表都使用UTF-8编码:
python复制app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://user:pass@host/db?charset=utf8mb4'
5.3 性能问题排查
使用Flask-SQLAlchemy的统计功能:
python复制app.config['SQLALCHEMY_RECORD_QUERIES'] = True
@app.after_request
def after_request(response):
for query in get_debug_queries():
if query.duration >= 0.5: # 超过0.5秒的查询
app.logger.warning(f'SLOW QUERY: {query.statement} {query.parameters} {query.duration}')
return response
6. 生产环境最佳实践
6.1 数据库迁移管理
使用Flask-Migrate进行数据库版本控制:
bash复制pip install flask-migrate
初始化:
python复制from flask_migrate import Migrate
migrate = Migrate(app, db)
命令行操作:
bash复制flask db init
flask db migrate -m "initial migration"
flask db upgrade
6.2 读写分离配置
对于高负载应用,可以配置读写分离:
python复制SQLALCHEMY_BINDS = {
'master': 'mysql+pymysql://master_user:pass@master_host/db',
'slave': 'mysql+pymysql://slave_user:pass@slave_host/db'
}
class User(db.Model):
__bind_key__ = 'master' # 默认写操作使用master
# 模型定义...
# 读操作可以指定使用slave
users = db.session().using_bind('slave').query(User).all()
6.3 监控与维护
推荐监控指标:
- 连接池使用情况
- 查询响应时间
- 慢查询日志
- 数据库负载
可以使用如下工具:
- Prometheus + Grafana
- MySQL Enterprise Monitor
- Percona Monitoring and Management
在实际项目中,我发现ORM的使用虽然带来了便利,但也容易导致开发者忽视底层SQL的性能特征。建议在开发后期阶段,对关键查询进行SQL分析和优化。Flask-SQLAlchemy提供的explain()方法非常有用:
python复制query = User.query.filter_by(active=True)
print(query.statement) # 查看生成的SQL
print(query.explain()) # 查看执行计划
另一个实用技巧是使用混合属性(Hybrid Attributes)来简化复杂查询:
python复制class User(db.Model):
@hybrid_property
def name_length(self):
return len(self.username)
@name_length.expression
def name_length(cls):
return db.func.length(cls.username)
# 可以在查询中使用
users = User.query.filter(User.name_length > 5).all()
