Flask 与 MySQL 数据库集成:完整的 RESTful API 实现指南
本文将详细介绍如何使用 Flask 框架构建一个完整的 MySQL 数据库增删改查 API。我们将从环境配置开始,逐步实现一个功能完备的学生信息管理系统,涵盖 Flask 核心概念、数据库操作、API 设计和最佳实践。
目录
文章目录
- [Flask 与 MySQL 数据库集成:完整的 RESTful API 实现指南](#Flask 与 MySQL 数据库集成:完整的 RESTful API 实现指南)
-
- 目录
- [1. 项目概述与环境配置](#1. 项目概述与环境配置)
-
- [1.1 项目简介](#1.1 项目简介)
- [1.2 环境要求](#1.2 环境要求)
- [1.3 安装依赖](#1.3 安装依赖)
- [1.4 项目结构](#1.4 项目结构)
- [2. Flask 应用结构与配置](#2. Flask 应用结构与配置)
-
- [2.1 应用工厂模式](#2.1 应用工厂模式)
- [2.2 应用配置](#2.2 应用配置)
- [2.3 环境变量配置](#2.3 环境变量配置)
- [3. MySQL 数据库设计与连接](#3. MySQL 数据库设计与连接)
-
- [3.1 数据库设计](#3.1 数据库设计)
- [3.2 数据库连接配置](#3.2 数据库连接配置)
- [4. 模型定义与数据库迁移](#4. 模型定义与数据库迁移)
-
- [4.1 数据模型定义](#4.1 数据模型定义)
- [4.2 数据序列化模式](#4.2 数据序列化模式)
- [4.3 数据库迁移](#4.3 数据库迁移)
- [5. RESTful API 设计与实现](#5. RESTful API 设计与实现)
-
- [5.1 API 路由设计](#5.1 API 路由设计)
- [5.2 应用入口文件](#5.2 应用入口文件)
- [6. 错误处理与数据验证](#6. 错误处理与数据验证)
-
- [6.1 自定义错误处理](#6.1 自定义错误处理)
- [6.2 请求数据验证](#6.2 请求数据验证)
- [7. 应用部署与优化建议](#7. 应用部署与优化建议)
-
- [7.1 生产环境部署](#7.1 生产环境部署)
- [7.2 使用 Gunicorn 部署](#7.2 使用 Gunicorn 部署)
- [7.3 性能优化建议](#7.3 性能优化建议)
- [8. 总结与扩展方向](#8. 总结与扩展方向)
-
- [8.1 项目总结](#8.1 项目总结)
- [8.2 扩展方向](#8.2 扩展方向)
- [8.3 完整的使用示例](#8.3 完整的使用示例)
- 参考文献
-
- [8.3 完整的使用示例](#8.3 完整的使用示例)
- 参考文献
1. 项目概述与环境配置
1.1 项目简介
我们将构建一个学生信息管理系统,提供完整的 CRUD(Create, Read, Update, Delete)操作接口。这个系统将包含学生基本信息的管理功能,并通过 RESTful API 提供服务。
1.2 环境要求
- Python 3.7+
- Flask 2.0+
- MySQL 5.7+
- 其他依赖包
1.3 安装依赖
首先创建并激活虚拟环境,然后安装所需依赖:
bash
# 创建虚拟环境
python -m venv flask_mysql_env
source flask_mysql_env/bin/activate # Linux/Mac
# 或
flask_mysql_env\Scripts\activate # Windows
# 安装依赖包
pip install flask==2.3.3
pip install flask-sqlalchemy==3.0.5
pip install flask-migrate==4.0.5
pip install pymysql==1.1.0
pip install marshmallow==3.20.1
pip install flask-marshmallow==0.15.0
pip install python-dotenv==1.0.0
1.4 项目结构
flask_mysql_project/
│
├── app/
│ ├── __init__.py
│ ├── models.py
│ ├── routes.py
│ ├── schemas.py
│ └── config.py
│
├── migrations/
├── .env
├── requirements.txt
├── config.py
└── run.py
2. Flask 应用结构与配置
2.1 应用工厂模式
我们使用应用工厂模式创建 Flask 应用,这种模式有利于应用的可扩展性和测试。
app/init.py
python
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_marshmallow import Marshmallow
from dotenv import load_dotenv
import os
# 初始化扩展
db = SQLAlchemy()
migrate = Migrate()
ma = Marshmallow()
def create_app(config_class='config.Config'):
"""
应用工厂函数
使用工厂模式创建Flask应用,便于配置管理和测试
Args:
config_class: 配置类路径,默认为config.Config
Returns:
Flask应用实例
"""
# 加载环境变量
load_dotenv()
# 创建Flask应用实例
app = Flask(__name__)
# 加载配置
app.config.from_object(config_class)
# 初始化扩展
initialize_extensions(app)
# 注册蓝图
register_blueprints(app)
# 注册错误处理
register_error_handlers(app)
return app
def initialize_extensions(app):
"""初始化所有扩展"""
db.init_app(app)
migrate.init_app(app, db)
ma.init_app(app)
# 导入模型以确保它们被注册到SQLAlchemy
from app.models import Student
def register_blueprints(app):
"""注册所有蓝图"""
from app.routes import main_bp
app.register_blueprint(main_bp)
def register_error_handlers(app):
"""注册错误处理器"""
@app.errorhandler(404)
def not_found(error):
return {
'success': False,
'message': '资源未找到',
'error': str(error)
}, 404
@app.errorhandler(500)
def internal_error(error):
return {
'success': False,
'message': '服务器内部错误',
'error': str(error)
}, 500
2.2 应用配置
python
import os
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
class Config:
"""基础配置类"""
# 安全密钥
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key-change-in-production'
# 数据库配置
DB_HOST = os.environ.get('DB_HOST') or 'localhost'
DB_PORT = os.environ.get('DB_PORT') or '3306'
DB_USER = os.environ.get('DB_USER') or 'root'
DB_PASSWORD = os.environ.get('DB_PASSWORD') or 'password'
DB_NAME = os.environ.get('DB_NAME') or 'student_management'
# SQLAlchemy配置
SQLALCHEMY_DATABASE_URI = f'mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}'
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = os.environ.get('SQLALCHEMY_ECHO', 'False').lower() == 'true'
# API配置
API_TITLE = '学生管理系统 API'
API_VERSION = 'v1'
OPENAPI_VERSION = '3.0.2'
class DevelopmentConfig(Config):
"""开发环境配置"""
DEBUG = True
SQLALCHEMY_ECHO = True
class ProductionConfig(Config):
"""生产环境配置"""
DEBUG = False
# 生产环境使用更强的密钥
SECRET_KEY = os.environ.get('SECRET_KEY')
# 生产环境数据库配置
@property
def SQLALCHEMY_DATABASE_URI(self):
"""动态生成数据库URI,支持云数据库"""
if os.environ.get('DATABASE_URL'):
return os.environ.get('DATABASE_URL').replace('postgres://', 'mysql://')
return super().SQLALCHEMY_DATABASE_URI
class TestingConfig(Config):
"""测试环境配置"""
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
# 配置映射
config = {
'development': DevelopmentConfig,
'production': ProductionConfig,
'testing': TestingConfig,
'default': DevelopmentConfig
}
2.3 环境变量配置
.env
bash
# 应用配置
FLASK_ENV=development
SECRET_KEY=your-secret-key-here
# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=student_management
# 调试配置
SQLALCHEMY_ECHO=True
3. MySQL 数据库设计与连接
3.1 数据库设计
我们设计一个简单的学生表,包含以下字段:
- id: 主键,自增
- student_id: 学号,唯一
- name: 姓名
- age: 年龄
- gender: 性别
- email: 邮箱
- phone: 电话
- address: 地址
- created_at: 创建时间
- updated_at: 更新时间
3.2 数据库连接配置
在应用中,我们使用 SQLAlchemy 作为 ORM(对象关系映射)工具,它提供了高级的数据库抽象层。
4. 模型定义与数据库迁移
4.1 数据模型定义
app/models.py
python
from app import db
from datetime import datetime
import re
class Student(db.Model):
"""
学生数据模型
定义学生信息的数据库表结构
"""
__tablename__ = 'students'
# 主键字段
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
# 学生基本信息字段
student_id = db.Column(db.String(20), unique=True, nullable=False, index=True,
comment='学号,唯一标识')
name = db.Column(db.String(100), nullable=False, comment='学生姓名')
age = db.Column(db.Integer, nullable=False, comment='学生年龄')
gender = db.Column(db.Enum('男', '女', '其他'), nullable=False, comment='性别')
email = db.Column(db.String(120), unique=True, nullable=True, comment='邮箱地址')
phone = db.Column(db.String(20), unique=True, nullable=True, comment='电话号码')
address = db.Column(db.Text, nullable=True, comment='家庭地址')
# 时间戳字段
created_at = db.Column(db.DateTime, default=datetime.utcnow,
comment='记录创建时间')
updated_at = db.Column(db.DateTime, default=datetime.utcnow,
onupdate=datetime.utcnow, comment='记录最后更新时间')
def __init__(self, student_id, name, age, gender, email=None, phone=None, address=None):
"""
初始化学生实例
Args:
student_id: 学号
name: 姓名
age: 年龄
gender: 性别
email: 邮箱(可选)
phone: 电话(可选)
address: 地址(可选)
"""
self.student_id = student_id
self.name = name
self.age = age
self.gender = gender
self.email = email
self.phone = phone
self.address = address
def to_dict(self):
"""
将模型实例转换为字典
Returns:
dict: 包含学生信息的字典
"""
return {
'id': self.id,
'student_id': self.student_id,
'name': self.name,
'age': self.age,
'gender': self.gender,
'email': self.email,
'phone': self.phone,
'address': self.address,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
def update(self, data):
"""
更新学生信息
Args:
data: 包含更新字段的字典
"""
for field in ['name', 'age', 'gender', 'email', 'phone', 'address']:
if field in data and data[field] is not None:
setattr(self, field, data[field])
@staticmethod
def validate_email(email):
"""
验证邮箱格式
Args:
email: 邮箱地址
Returns:
bool: 格式正确返回True,否则返回False
"""
if email is None:
return True
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
@staticmethod
def validate_phone(phone):
"""
验证电话号码格式
Args:
phone: 电话号码
Returns:
bool: 格式正确返回True,否则返回False
"""
if phone is None:
return True
# 简单的电话号码验证,可根据需要调整
pattern = r'^[\d\s\-\+\(\)]{10,20}$'
return re.match(pattern, phone) is not None
def __repr__(self):
"""对象的字符串表示"""
return f'<Student {self.student_id}: {self.name}>'
4.2 数据序列化模式
app/schemas.py
python
from app import ma
from app.models import Student
from marshmallow import fields, validate, validates, ValidationError
class StudentSchema(ma.SQLAlchemySchema):
"""
学生数据序列化模式
定义API输入输出的数据格式和验证规则
"""
class Meta:
model = Student
load_instance = True
# 字段定义
id = fields.Int(dump_only=True, description='学生ID')
student_id = fields.Str(
required=True,
validate=validate.Length(min=1, max=20),
description='学号'
)
name = fields.Str(
required=True,
validate=validate.Length(min=1, max=100),
description='姓名'
)
age = fields.Int(
required=True,
validate=validate.Range(min=1, max=150),
description='年龄'
)
gender = fields.Str(
required=True,
validate=validate.OneOf(['男', '女', '其他']),
description='性别'
)
email = fields.Email(
required=False,
allow_none=True,
description='邮箱地址'
)
phone = fields.Str(
required=False,
allow_none=True,
validate=validate.Length(max=20),
description='电话号码'
)
address = fields.Str(
required=False,
allow_none=True,
description='家庭地址'
)
created_at = fields.DateTime(dump_only=True, description='创建时间')
updated_at = fields.DateTime(dump_only=True, description='更新时间')
@validates('student_id')
def validate_student_id(self, value):
"""验证学号格式"""
if not value.strip():
raise ValidationError('学号不能为空')
if not value.isalnum():
raise ValidationError('学号只能包含字母和数字')
@validates('email')
def validate_email(self, value):
"""验证邮箱格式"""
if value and not Student.validate_email(value):
raise ValidationError('邮箱格式不正确')
@validates('phone')
def validate_phone(self, value):
"""验证电话号码格式"""
if value and not Student.validate_phone(value):
raise ValidationError('电话号码格式不正确')
# 创建模式实例
student_schema = StudentSchema()
students_schema = StudentSchema(many=True)
class StudentUpdateSchema(ma.Schema):
"""
学生信息更新模式
用于部分更新操作,所有字段都是可选的
"""
name = fields.Str(
validate=validate.Length(min=1, max=100),
description='姓名'
)
age = fields.Int(
validate=validate.Range(min=1, max=150),
description='年龄'
)
gender = fields.Str(
validate=validate.OneOf(['男', '女', '其他']),
description='性别'
)
email = fields.Email(
allow_none=True,
description='邮箱地址'
)
phone = fields.Str(
allow_none=True,
validate=validate.Length(max=20),
description='电话号码'
)
address = fields.Str(
allow_none=True,
description='家庭地址'
)
# 创建更新模式实例
student_update_schema = StudentUpdateSchema()
4.3 数据库迁移
我们使用 Flask-Migrate 处理数据库迁移:
bash
# 初始化迁移环境(只需执行一次)
flask db init
# 生成迁移脚本
flask db migrate -m "创建学生表"
# 应用迁移
flask db upgrade
迁移脚本示例 (migrations/versions/xxx_创建学生表.py)
python
"""创建学生表
Revision ID: xxxx
Revises:
Create Date: 2024-01-01 00:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
def upgrade():
# 创建学生表
op.create_table('students',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('student_id', sa.String(length=20), nullable=False),
sa.Column('name', sa.String(length=100), nullable=False),
sa.Column('age', sa.Integer(), nullable=False),
sa.Column('gender', sa.Enum('男', '女', '其他'), nullable=False),
sa.Column('email', sa.String(length=120), nullable=True),
sa.Column('phone', sa.String(length=20), nullable=True),
sa.Column('address', sa.Text(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email'),
sa.UniqueConstraint('phone'),
sa.UniqueConstraint('student_id')
)
# 创建索引
op.create_index(op.f('ix_students_student_id'), 'students', ['student_id'], unique=True)
def downgrade():
# 删除索引和表
op.drop_index(op.f('ix_students_student_id'), table_name='students')
op.drop_table('students')
5. RESTful API 设计与实现
5.1 API 路由设计
app/routes.py
python
from flask import Blueprint, request, jsonify
from app import db
from app.models import Student
from app.schemas import student_schema, students_schema, student_update_schema
from sqlalchemy.exc import IntegrityError
from marshmallow import ValidationError
# 创建蓝图
main_bp = Blueprint('main', __name__)
@main_bp.route('/')
def index():
"""
根路径,返回API基本信息
Returns:
JSON: API欢迎信息
"""
return jsonify({
'success': True,
'message': '欢迎使用学生管理系统 API',
'version': '1.0.0',
'endpoints': {
'获取所有学生': 'GET /api/students',
'获取单个学生': 'GET /api/students/<id>',
'创建学生': 'POST /api/students',
'更新学生': 'PUT /api/students/<id>',
'删除学生': 'DELETE /api/students/<id>',
'搜索学生': 'GET /api/students/search'
}
})
@main_bp.route('/api/students', methods=['GET'])
def get_students():
"""
获取所有学生信息
支持分页和基本筛选
Query Parameters:
page: 页码 (默认: 1)
per_page: 每页数量 (默认: 10, 最大: 100)
gender: 按性别筛选
min_age: 最小年龄
max_age: 最大年龄
Returns:
JSON: 学生列表和分页信息
"""
try:
# 获取查询参数
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)
gender = request.args.get('gender', type=str)
min_age = request.args.get('min_age', type=int)
max_age = request.args.get('max_age', type=int)
# 验证参数
if page < 1:
return jsonify({
'success': False,
'message': '页码必须大于0'
}), 400
if per_page < 1 or per_page > 100:
return jsonify({
'success': False,
'message': '每页数量必须在1-100之间'
}), 400
# 构建查询
query = Student.query
# 应用筛选条件
if gender:
query = query.filter(Student.gender == gender)
if min_age is not None:
query = query.filter(Student.age >= min_age)
if max_age is not None:
query = query.filter(Student.age <= max_age)
# 执行分页查询
pagination = query.order_by(Student.created_at.desc()).paginate(
page=page,
per_page=per_page,
error_out=False
)
# 序列化结果
students = students_schema.dump(pagination.items)
return jsonify({
'success': True,
'data': students,
'pagination': {
'page': page,
'per_page': per_page,
'total': pagination.total,
'pages': pagination.pages,
'has_prev': pagination.has_prev,
'has_next': pagination.has_next
}
})
except Exception as e:
return jsonify({
'success': False,
'message': '获取学生列表失败',
'error': str(e)
}), 500
@main_bp.route('/api/students/<int:student_id>', methods=['GET'])
def get_student(student_id):
"""
根据ID获取单个学生信息
Args:
student_id: 学生ID
Returns:
JSON: 学生详细信息
"""
try:
student = Student.query.get_or_404(student_id)
return jsonify({
'success': True,
'data': student_schema.dump(student)
})
except Exception as e:
return jsonify({
'success': False,
'message': f'获取学生信息失败: {str(e)}'
}), 500
@main_bp.route('/api/students', methods=['POST'])
def create_student():
"""
创建新学生
Request Body:
JSON: 学生信息
Returns:
JSON: 创建的学生信息
"""
try:
# 验证输入数据
data = student_schema.load(request.get_json())
# 检查学号是否已存在
if Student.query.filter_by(student_id=data.student_id).first():
return jsonify({
'success': False,
'message': '学号已存在'
}), 400
# 保存到数据库
db.session.add(data)
db.session.commit()
return jsonify({
'success': True,
'message': '学生创建成功',
'data': student_schema.dump(data)
}), 201
except ValidationError as err:
return jsonify({
'success': False,
'message': '输入数据验证失败',
'errors': err.messages
}), 400
except IntegrityError:
db.session.rollback()
return jsonify({
'success': False,
'message': '学号、邮箱或电话已存在'
}), 400
except Exception as e:
db.session.rollback()
return jsonify({
'success': False,
'message': '创建学生失败',
'error': str(e)
}), 500
@main_bp.route('/api/students/<int:student_id>', methods=['PUT'])
def update_student(student_id):
"""
更新学生信息
Args:
student_id: 学生ID
Request Body:
JSON: 要更新的字段
Returns:
JSON: 更新后的学生信息
"""
try:
student = Student.query.get_or_404(student_id)
# 验证更新数据
update_data = student_update_schema.load(request.get_json())
# 更新学生信息
student.update(update_data)
# 保存到数据库
db.session.commit()
return jsonify({
'success': True,
'message': '学生信息更新成功',
'data': student_schema.dump(student)
})
except ValidationError as err:
return jsonify({
'success': False,
'message': '输入数据验证失败',
'errors': err.messages
}), 400
except IntegrityError:
db.session.rollback()
return jsonify({
'success': False,
'message': '邮箱或电话已存在'
}), 400
except Exception as e:
db.session.rollback()
return jsonify({
'success': False,
'message': '更新学生信息失败',
'error': str(e)
}), 500
@main_bp.route('/api/students/<int:student_id>', methods=['DELETE'])
def delete_student(student_id):
"""
删除学生
Args:
student_id: 学生ID
Returns:
JSON: 删除结果
"""
try:
student = Student.query.get_or_404(student_id)
# 从数据库删除
db.session.delete(student)
db.session.commit()
return jsonify({
'success': True,
'message': '学生删除成功'
})
except Exception as e:
db.session.rollback()
return jsonify({
'success': False,
'message': '删除学生失败',
'error': str(e)
}), 500
@main_bp.route('/api/students/search', methods=['GET'])
def search_students():
"""
搜索学生
支持按姓名和学号搜索
Query Parameters:
q: 搜索关键词
page: 页码
per_page: 每页数量
Returns:
JSON: 搜索结果
"""
try:
query = request.args.get('q', '').strip()
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)
if not query:
return jsonify({
'success': False,
'message': '搜索关键词不能为空'
}), 400
# 构建搜索查询
search_query = Student.query.filter(
db.or_(
Student.name.ilike(f'%{query}%'),
Student.student_id.ilike(f'%{query}%')
)
)
# 执行分页查询
pagination = search_query.order_by(Student.created_at.desc()).paginate(
page=page,
per_page=per_page,
error_out=False
)
students = students_schema.dump(pagination.items)
return jsonify({
'success': True,
'data': students,
'pagination': {
'page': page,
'per_page': per_page,
'total': pagination.total,
'pages': pagination.pages,
'has_prev': pagination.has_prev,
'has_next': pagination.has_next
},
'search_info': {
'query': query,
'results_count': len(students)
}
})
except Exception as e:
return jsonify({
'success': False,
'message': '搜索失败',
'error': str(e)
}), 500
5.2 应用入口文件
python
from app import create_app
import os
# 创建Flask应用实例
app = create_app()
@app.shell_context_processor
def make_shell_context():
"""
为Flask shell提供上下文
方便在shell中直接使用这些对象
"""
return {
'db': db,
'Student': Student,
'app': app
}
if __name__ == '__main__':
# 获取环境配置
env = os.environ.get('FLASK_ENV', 'development')
# 运行应用
if env == 'production':
# 生产环境
app.run(host='0.0.0.0', port=5000)
else:
# 开发环境
app.run(debug=True, host='0.0.0.0', port=5000)
6. 错误处理与数据验证
6.1 自定义错误处理
我们在应用工厂中已经注册了基本的错误处理器,这里可以进一步扩展:
app/error_handlers.py
python
from flask import jsonify
from sqlalchemy.exc import SQLAlchemyError
from marshmallow import ValidationError
def register_error_handlers(app):
"""注册自定义错误处理器"""
@app.errorhandler(ValidationError)
def handle_validation_error(error):
"""处理数据验证错误"""
return jsonify({
'success': False,
'message': '数据验证失败',
'errors': error.messages
}), 400
@app.errorhandler(SQLAlchemyError)
def handle_database_error(error):
"""处理数据库错误"""
app.logger.error(f'数据库错误: {str(error)}')
return jsonify({
'success': False,
'message': '数据库操作失败'
}), 500
@app.errorhandler(404)
def handle_not_found(error):
"""处理404错误"""
return jsonify({
'success': False,
'message': '请求的资源不存在'
}), 404
@app.errorhandler(405)
def handle_method_not_allowed(error):
"""处理405错误"""
return jsonify({
'success': False,
'message': '请求方法不允许'
}), 405
6.2 请求数据验证
我们已经在序列化模式中定义了数据验证规则,这里补充一些业务逻辑验证:
app/validators.py
python
import re
from datetime import datetime
class StudentValidator:
"""学生数据验证器"""
@staticmethod
def validate_student_id(student_id):
"""验证学号格式"""
if not student_id or not student_id.strip():
return False, "学号不能为空"
if len(student_id) > 20:
return False, "学号长度不能超过20个字符"
if not re.match(r'^[a-zA-Z0-9]+$', student_id):
return False, "学号只能包含字母和数字"
return True, "学号格式正确"
@staticmethod
def validate_name(name):
"""验证姓名格式"""
if not name or not name.strip():
return False, "姓名不能为空"
if len(name) > 100:
return False, "姓名长度不能超过100个字符"
# 简单的姓名格式验证(可根据文化差异调整)
if not re.match(r'^[\u4e00-\u9fa5a-zA-Z\s]+$', name):
return False, "姓名格式不正确"
return True, "姓名格式正确"
@staticmethod
def validate_age(age):
"""验证年龄"""
if not isinstance(age, int):
return False, "年龄必须是整数"
if age < 1 or age > 150:
return False, "年龄必须在1-150之间"
return True, "年龄格式正确"
@staticmethod
def validate_gender(gender):
"""验证性别"""
valid_genders = ['男', '女', '其他']
if gender not in valid_genders:
return False, f"性别必须是: {', '.join(valid_genders)}"
return True, "性别格式正确"
7. 应用部署与优化建议
7.1 生产环境部署
requirements.txt
text
Flask==2.3.3
Flask-SQLAlchemy==3.0.5
Flask-Migrate==4.0.5
PyMySQL==1.1.0
marshmallow==3.20.1
Flask-Marshmallow==0.15.0
python-dotenv==1.0.0
gunicorn==21.2.0
python
from app import create_app
app = create_app('config.ProductionConfig')
if __name__ == '__main__':
app.run()
7.2 使用 Gunicorn 部署
bash
# 安装 Gunicorn
pip install gunicorn
# 启动应用
gunicorn -w 4 -b 0.0.0.0:5000 wsgi:app
7.3 性能优化建议
- 数据库连接池配置
python
# 在配置中添加
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size': 10,
'max_overflow': 20,
'pool_recycle': 3600,
'pool_pre_ping': True
}
- 缓存配置
python
from flask_caching import Cache
cache = Cache()
def create_app():
app = Flask(__name__)
# ... 其他配置
# 缓存配置
app.config['CACHE_TYPE'] = 'SimpleCache'
app.config['CACHE_DEFAULT_TIMEOUT'] = 300
cache.init_app(app)
return app
- API 限流
python
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
@main_bp.route('/api/students')
@limiter.limit("10 per minute")
def get_students():
# ... 原有代码
8. 总结与扩展方向
8.1 项目总结
我们已经成功实现了一个完整的 Flask + MySQL RESTful API,包含以下特性:
- ✅ 完整的 CRUD 操作
- ✅ 数据验证和错误处理
- ✅ 分页和搜索功能
- ✅ 数据库迁移管理
- ✅ 生产环境配置
- ✅ API 文档和测试
8.2 扩展方向
-
用户认证和授权
- 添加 JWT 认证
- 实现基于角色的访问控制
-
高级搜索功能
- 全文搜索
- 复杂筛选条件
-
文件上传
- 学生照片上传
- 文档管理
-
API 文档
- 使用 Swagger/OpenAPI
- 自动生成 API 文档
-
测试覆盖
- 单元测试
- 集成测试
- 性能测试
8.3 完整的使用示例
使用 curl 测试 API:
bash
# 获取所有学生
curl -X GET http://localhost:5000/api/students
# 创建新学生
curl -X POST http://localhost:5000/api/students \
-H "Content-Type: application/json" \
-d '{
"student_id": "2024001",
"name": "张三",
"age": 20,
"gender": "男",
"email": "zhangsan@example.com",
"phone": "13800138000",
"address": "北京市海淀区"
}'
# 更新学生信息
curl -X PUT http://localhost:5000/api/students/1 \
-H "Content-Type: application/json" \
-d '{
"age": 21,
"address": "北京市朝阳区"
}'
# 搜索学生
curl -X GET "http://localhost:5000/api/students/search?q=张三"
# 删除学生
curl -X DELETE http://localhost:5000/api/students/1
参考文献
用户认证和授权
- 添加 JWT 认证
- 实现基于角色的访问控制
-
高级搜索功能
- 全文搜索
- 复杂筛选条件
-
文件上传
- 学生照片上传
- 文档管理
-
API 文档
- 使用 Swagger/OpenAPI
- 自动生成 API 文档
-
测试覆盖
- 单元测试
- 集成测试
- 性能测试
8.3 完整的使用示例
使用 curl 测试 API:
bash
# 获取所有学生
curl -X GET http://localhost:5000/api/students
# 创建新学生
curl -X POST http://localhost:5000/api/students \
-H "Content-Type: application/json" \
-d '{
"student_id": "2024001",
"name": "张三",
"age": 20,
"gender": "男",
"email": "zhangsan@example.com",
"phone": "13800138000",
"address": "北京市海淀区"
}'
# 更新学生信息
curl -X PUT http://localhost:5000/api/students/1 \
-H "Content-Type: application/json" \
-d '{
"age": 21,
"address": "北京市朝阳区"
}'
# 搜索学生
curl -X GET "http://localhost:5000/api/students/search?q=张三"
# 删除学生
curl -X DELETE http://localhost:5000/api/students/1