1. Flask与MySQL数据库连接实战
在Web开发中,数据库操作是后端开发的核心环节。作为Python轻量级框架的Flask,与MySQL这类关系型数据库的配合使用非常普遍。我最近在重构一个内部管理系统时,就采用了Flask+MySQL的技术栈,这里分享下我的实战经验。
Flask连接MySQL主要有两种方式:直接使用pymysql驱动和使用ORM框架。对于中小型项目,我强烈推荐使用ORM方式,特别是Flask-SQLAlchemy这个扩展。它不仅简化了数据库操作,还能有效防止SQL注入,让代码更易维护。下面我会从环境准备开始,逐步演示完整的集成流程。
提示:在实际项目中,建议从一开始就使用ORM,即使初期需求简单。随着业务复杂度的增加,ORM的优势会越来越明显。
1.1 环境准备与依赖安装
首先需要确保Python环境已就绪(建议3.7+版本),然后通过pip安装必要的包:
bash复制pip install flask flask-sqlalchemy pymysql cryptography
这里解释下各包的作用:
flask: 核心框架flask-sqlalchemy: Flask专用的SQLAlchemy集成pymysql: MySQL的Python驱动cryptography: 用于安全连接(某些MySQL版本需要)
对于MySQL服务端,可以使用本地安装的MySQL或云数据库服务。我推荐开发阶段使用Docker快速启动MySQL:
bash复制docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 -d mysql:8.0
1.2 基础配置与连接建立
在Flask应用中配置数据库连接,建议将配置信息放在config.py中:
python复制# config.py
class Config:
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://username:password@localhost:3306/db_name'
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = True # 开发时开启,可查看生成的SQL
然后在应用工厂函数中初始化:
python复制from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config.from_object('config.Config')
db.init_app(app)
with app.app_context():
db.create_all() # 创建数据表
return app
注意:SQLALCHEMY_TRACK_MODIFICATIONS建议设为False以避免不必要的内存开销。SQLALCHEMY_ECHO仅在调试时开启,生产环境应关闭。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. ORM模型定义与关系映射
2.1 基础模型定义
使用SQLAlchemy定义模型时,每个类对应数据库中的一张表。以下是一个用户模型的示例:
python复制from datetime import datetime
from . import db
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}>'
字段类型与MySQL的对应关系:
- Integer → INT
- String → VARCHAR
- DateTime → DATETIME
- Boolean → TINYINT(1)
2.2 高级字段选项
实际项目中,字段通常需要更多约束:
python复制from werkzeug.security import generate_password_hash, check_password_hash
class User(db.Model):
# ... 其他字段同上
_password = db.Column('password', db.String(256), nullable=False)
@property
def password(self):
return self._password
@password.setter
def password(self, value):
self._password = generate_password_hash(value)
def check_password(self, password):
return check_password_hash(self._password, password)
这样处理密码可以确保安全存储。注意这里使用了werkzeug的安全工具,它是Flask内置的。
2.3 模型关系定义
关系型数据库的核心价值在于表间关系。SQLAlchemy支持所有标准关系:
python复制class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100))
content = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
author = db.relationship('User', backref=db.backref('posts', lazy=True))
class Comment(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text)
post_id = db.Column(db.Integer, db.ForeignKey('posts.id'))
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
post = db.relationship('Post', backref=db.backref('comments', lazy=True))
user = db.relationship('User', backref=db.backref('comments', lazy=True))
关系类型说明:
- 一对多:使用ForeignKey+relationship
- 多对多:需要中间关联表
- 一对一:添加uselist=False参数
3. CRUD操作全解析
3.1 创建数据
基本创建操作:
python复制from .models import User
from . import db
new_user = User(username='john', email='john@example.com')
new_user.password = 'securepassword' # 会自动哈希
db.session.add(new_user)
db.session.commit()
批量创建更高效:
python复制users = [
User(username='user1', email='user1@test.com'),
User(username='user2', email='user2@test.com')
]
db.session.bulk_save_objects(users)
db.session.commit()
经验:对于大量插入,使用bulk_save_objects比单独add每个对象快10倍以上。
3.2 查询数据
基础查询:
python复制# 获取所有用户
users = User.query.all()
# 获取单个用户
user = User.query.get(1) # 按主键查询
# 条件查询
admin_users = User.query.filter_by(is_admin=True).all()
复杂查询:
python复制from sqlalchemy import or_
# 分页查询
page = User.query.order_by(User.created_at.desc()).paginate(page=1, per_page=20)
# 多条件OR查询
search_results = User.query.filter(
or_(
User.username.like('%john%'),
User.email.like('%example.com%')
)
).all()
# 关联查询
posts_with_author = Post.query.join(User).filter(User.username == 'john').all()
3.3 更新数据
直接修改属性后提交:
python复制user = User.query.get(1)
user.email = 'new@email.com'
db.session.commit()
批量更新更高效:
python复制User.query.filter_by(is_active=False).update({'is_active': True})
db.session.commit()
3.4 删除数据
单个删除:
python复制user = User.query.get(1)
db.session.delete(user)
db.session.commit()
批量删除:
python复制User.query.filter_by(is_spam=True).delete()
db.session.commit()
4. 高级技巧与性能优化
4.1 连接池配置
生产环境中,连接池配置很重要:
python复制app.config['SQLALCHEMY_POOL_SIZE'] = 20
app.config['SQLALCHEMY_POOL_TIMEOUT'] = 30
app.config['SQLALCHEMY_POOL_RECYCLE'] = 3600 # 1小时后回收连接
4.2 事务管理
复杂操作应使用事务:
python复制try:
# 操作1
user1 = User(username='user1')
db.session.add(user1)
# 操作2
user2 = User(username='user2')
db.session.add(user2)
db.session.commit()
except Exception as e:
db.session.rollback()
raise e
4.3 查询优化技巧
- 避免N+1查询:
python复制# 不好的方式:会导致N+1查询问题
posts = Post.query.all()
for post in posts:
print(post.author.username) # 每次循环都查询一次作者
# 好的方式:使用joinedload
from sqlalchemy.orm import joinedload
posts = Post.query.options(joinedload(Post.author)).all()
- 只选择需要的列:
python复制# 不推荐
users = User.query.all() # 选择所有列
# 推荐
users = User.query.with_entities(User.id, User.username).all()
- 使用索引提示:
python复制from sqlalchemy import text
User.query.from_statement(
text('SELECT * FROM users USE INDEX (idx_username) WHERE username=:name')
).params(name='john').all()
5. 常见问题排查
5.1 连接问题
错误:"Can't connect to MySQL server"
解决方案:
- 检查MySQL服务是否运行
- 验证连接字符串(用户名、密码、主机、端口)
- 检查防火墙设置
- 对于云数据库,确认网络策略允许访问
5.2 编码问题
错误:"Incorrect string value"
解决方案:
- 确保数据库、表和字段使用utf8mb4编码
- 在连接字符串中添加charset参数:
mysql+pymysql://user:pass@host/db?charset=utf8mb4
5.3 性能问题
现象:查询缓慢
排查步骤:
- 使用EXPLAIN分析慢查询
- 检查是否缺少索引
- 优化复杂联表查询
- 考虑添加缓存层
5.4 会话管理
错误:"This Session's transaction has been rolled back"
解决方案:
- 确保每个请求后正确关闭session
- 避免长时间持有session
- 使用app.teardown_appcontext自动清理:
python复制@app.teardown_appcontext
def shutdown_session(exception=None):
db.session.remove()
6. 项目结构建议
对于大型项目,推荐这样组织代码:
code复制/myapp
/config.py
/extensions.py # 存放db等扩展实例
/models
/__init__.py # 暴露所有模型
/user.py
/post.py
/services
/user_service.py # 业务逻辑
/app.py # 应用工厂
这种结构的好处:
- 模型与业务逻辑分离
- 避免循环导入
- 便于单元测试
- 支持蓝本模块化
在user_service.py中封装业务逻辑:
python复制class UserService:
@staticmethod
def create_user(username, email, password):
user = User(username=username, email=email)
user.password = password
db.session.add(user)
db.session.commit()
return user
@staticmethod
def get_user_by_id(user_id):
return User.query.get(user_id)
这种模式使路由保持简洁,便于维护。
