flask
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFlask - Lightweight Python Web Framework
Flask - 轻量级Python Web框架
Overview
概述
Flask is a micro-framework for Python web development, designed for building microservices, REST APIs, and flexible web applications. Its minimalist core and extensive extension ecosystem make it ideal for projects requiring lightweight architecture, rapid development, and full control over components.
Key Features:
- Micro-framework philosophy (minimal core, extensible)
- Flask-RESTful for API development
- Blueprints for modular application structure
- SQLAlchemy integration via Flask-SQLAlchemy
- Jinja2 templating engine
- Built-in development server with auto-reload
- Werkzeug WSGI toolkit foundation
- Large extension ecosystem (Flask-Login, Flask-JWT, Flask-CORS)
- Production deployment with Gunicorn/uWSGI
Installation:
bash
undefinedFlask是一款用于Python Web开发的微框架,专为构建微服务、REST API和灵活的Web应用而设计。其极简的核心和丰富的扩展生态系统,使其成为需要轻量级架构、快速开发以及对组件完全控制的项目的理想选择。
核心特性:
- 微框架理念(极简核心,可扩展)
- 用于API开发的Flask-RESTful
- 用于模块化应用结构的Blueprints
- 通过Flask-SQLAlchemy集成SQLAlchemy
- Jinja2模板引擎
- 自带支持自动重载的开发服务器
- 基于Werkzeug WSGI工具包
- 庞大的扩展生态系统(Flask-Login、Flask-JWT、Flask-CORS等)
- 可通过Gunicorn/uWSGI部署到生产环境
安装:
bash
undefinedBasic Flask
基础Flask
pip install flask
pip install flask
Flask with common extensions
包含常用扩展的Flask
pip install flask flask-restful flask-sqlalchemy flask-login flask-cors
pip install flask flask-restful flask-sqlalchemy flask-login flask-cors
With database support
带数据库支持
pip install flask flask-sqlalchemy psycopg2-binary # PostgreSQL
pip install flask flask-sqlalchemy psycopg2-binary # PostgreSQL
Full microservices stack
完整微服务栈
pip install flask flask-restful marshmallow flask-jwt-extended redis
undefinedpip install flask flask-restful marshmallow flask-jwt-extended redis
undefinedBasic Application Patterns
基础应用模式
1. Minimal Flask App
1. 最简Flask应用
python
undefinedpython
undefinedapp.py
app.py
from flask import Flask, jsonify, request
app = Flask(name)
@app.route('/')
def hello():
return jsonify({"message": "Hello, World!"})
@app.route('/api/users/int:user_id')
def get_user(user_id):
return jsonify({"id": user_id, "name": f"User {user_id}"})
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.get_json()
return jsonify({"id": 123, **data}), 201
if name == 'main':
app.run(debug=True, host='0.0.0.0', port=5000)
**Run:**
```bashfrom flask import Flask, jsonify, request
app = Flask(name)
@app.route('/')
def hello():
return jsonify({"message": "Hello, World!"})
@app.route('/api/users/int:user_id')
def get_user(user_id):
return jsonify({"id": user_id, "name": f"User {user_id}"})
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.get_json()
return jsonify({"id": 123, **data}), 201
if name == 'main':
app.run(debug=True, host='0.0.0.0', port=5000)
**运行**:
```bashDevelopment server
开发服务器
python app.py
python app.py
Or using flask CLI
或使用Flask CLI
export FLASK_APP=app.py
export FLASK_ENV=development
flask run
export FLASK_APP=app.py
export FLASK_ENV=development
flask run
Custom port
自定义端口
flask run --port 8000 --host 0.0.0.0
undefinedflask run --port 8000 --host 0.0.0.0
undefined2. Application Factory Pattern
2. 应用工厂模式
Recommended for production and testing:
python
undefined推荐用于生产和测试环境:
python
undefinedapp/init.py
app/init.py
from flask import Flask
from app.config import Config
from app.extensions import db, migrate, jwt
def create_app(config_class=Config):
"""Application factory pattern."""
app = Flask(name)
app.config.from_object(config_class)
# Initialize extensions
db.init_app(app)
migrate.init_app(app, db)
jwt.init_app(app)
# Register blueprints
from app.routes import api_bp, auth_bp
app.register_blueprint(api_bp, url_prefix='/api')
app.register_blueprint(auth_bp, url_prefix='/auth')
return appfrom flask import Flask
from app.config import Config
from app.extensions import db, migrate, jwt
def create_app(config_class=Config):
"""应用工厂模式。"""
app = Flask(name)
app.config.from_object(config_class)
# 初始化扩展
db.init_app(app)
migrate.init_app(app, db)
jwt.init_app(app)
# 注册蓝图
from app.routes import api_bp, auth_bp
app.register_blueprint(api_bp, url_prefix='/api')
app.register_blueprint(auth_bp, url_prefix='/auth')
return appapp/extensions.py
app/extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_jwt_extended import JWTManager
db = SQLAlchemy()
migrate = Migrate()
jwt = JWTManager()
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_jwt_extended import JWTManager
db = SQLAlchemy()
migrate = Migrate()
jwt = JWTManager()
app/config.py
app/config.py
import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///app.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY') or 'jwt-secret'
class DevelopmentConfig(Config):
DEBUG = True
TESTING = False
class ProductionConfig(Config):
DEBUG = False
TESTING = False
import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///app.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY') or 'jwt-secret'
class DevelopmentConfig(Config):
DEBUG = True
TESTING = False
class ProductionConfig(Config):
DEBUG = False
TESTING = False
run.py
run.py
from app import create_app
app = create_app()
if name == 'main':
app.run()
**Run:**
```bash
export FLASK_APP=run.py
flask runfrom app import create_app
app = create_app()
if name == 'main':
app.run()
**运行**:
```bash
export FLASK_APP=run.py
flask run3. Request and Response Handling
3. 请求与响应处理
python
from flask import Flask, request, jsonify, make_response, abort
app = Flask(__name__)
@app.route('/api/data', methods=['GET', 'POST'])
def handle_data():
# GET request
if request.method == 'GET':
# Query parameters
page = request.args.get('page', 1, type=int)
limit = request.args.get('limit', 10, type=int)
return jsonify({
"page": page,
"limit": limit,
"data": [...]
})
# POST request
if request.method == 'POST':
# JSON body
data = request.get_json()
# Validation
if not data or 'name' not in data:
abort(400, description="Missing required field: name")
# Custom response with headers
response = make_response(jsonify({"id": 1, **data}), 201)
response.headers['X-Custom-Header'] = 'value'
return responsepython
from flask import Flask, request, jsonify, make_response, abort
app = Flask(__name__)
@app.route('/api/data', methods=['GET', 'POST'])
def handle_data():
# GET请求
if request.method == 'GET':
# 查询参数
page = request.args.get('page', 1, type=int)
limit = request.args.get('limit', 10, type=int)
return jsonify({
"page": page,
"limit": limit,
"data": [...]
})
# POST请求
if request.method == 'POST':
# JSON请求体
data = request.get_json()
# 验证
if not data or 'name' not in data:
abort(400, description="缺少必填字段: name")
# 带自定义头的响应
response = make_response(jsonify({"id": 1, **data}), 201)
response.headers['X-Custom-Header'] = 'value'
return responseError handling
错误处理
@app.errorhandler(404)
def not_found(error):
return jsonify({"error": "Resource not found"}), 404
@app.errorhandler(400)
def bad_request(error):
return jsonify({"error": str(error.description)}), 400
undefined@app.errorhandler(404)
def not_found(error):
return jsonify({"error": "资源未找到"}), 404
@app.errorhandler(400)
def bad_request(error):
return jsonify({"error": str(error.description)}), 400
undefinedBlueprints - Modular Applications
Blueprints - 模块化应用
Blueprint Structure
蓝图结构
app/
├── __init__.py # Application factory
├── extensions.py # Extension instances
├── config.py # Configuration
├── models/
│ ├── __init__.py
│ ├── user.py
│ └── product.py
├── routes/
│ ├── __init__.py
│ ├── auth.py # Authentication routes
│ ├── users.py # User management routes
│ └── products.py # Product routes
└── services/
├── __init__.py
├── user_service.py
└── auth_service.pyapp/
├── __init__.py # 应用工厂
├── extensions.py # 扩展实例
├── config.py # 配置文件
├── models/
│ ├── __init__.py
│ ├── user.py
│ └── product.py
├── routes/
│ ├── __init__.py
│ ├── auth.py # 认证路由
│ ├── users.py # 用户管理路由
│ └── products.py # 产品路由
└── services/
├── __init__.py
├── user_service.py
└── auth_service.pyBlueprint Implementation
蓝图实现
python
undefinedpython
undefinedapp/routes/users.py
app/routes/users.py
from flask import Blueprint, jsonify, request
from app.models.user import User
from app.extensions import db
users_bp = Blueprint('users', name)
@users_bp.route('/', methods=['GET'])
def list_users():
"""List all users."""
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 20, type=int)
users = User.query.paginate(page=page, per_page=per_page)
return jsonify({
"users": [u.to_dict() for u in users.items],
"total": users.total,
"page": users.page,
"pages": users.pages
})@users_bp.route('/int:user_id', methods=['GET'])
def get_user(user_id):
"""Get user by ID."""
user = User.query.get_or_404(user_id)
return jsonify(user.to_dict())
@users_bp.route('/', methods=['POST'])
def create_user():
"""Create new user."""
data = request.get_json()
user = User(
email=data['email'],
name=data['name']
)
user.set_password(data['password'])
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict()), 201@users_bp.route('/int:user_id', methods=['PUT'])
def update_user(user_id):
"""Update user."""
user = User.query.get_or_404(user_id)
data = request.get_json()
if 'name' in data:
user.name = data['name']
if 'email' in data:
user.email = data['email']
db.session.commit()
return jsonify(user.to_dict())@users_bp.route('/int:user_id', methods=['DELETE'])
def delete_user(user_id):
"""Delete user."""
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return '', 204from flask import Blueprint, jsonify, request
from app.models.user import User
from app.extensions import db
users_bp = Blueprint('users', name)
@users_bp.route('/', methods=['GET'])
def list_users():
"""列出所有用户。"""
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 20, type=int)
users = User.query.paginate(page=page, per_page=per_page)
return jsonify({
"users": [u.to_dict() for u in users.items],
"total": users.total,
"page": users.page,
"pages": users.pages
})@users_bp.route('/int:user_id', methods=['GET'])
def get_user(user_id):
"""根据ID获取用户。"""
user = User.query.get_or_404(user_id)
return jsonify(user.to_dict())
@users_bp.route('/', methods=['POST'])
def create_user():
"""创建新用户。"""
data = request.get_json()
user = User(
email=data['email'],
name=data['name']
)
user.set_password(data['password'])
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict()), 201@users_bp.route('/int:user_id', methods=['PUT'])
def update_user(user_id):
"""更新用户信息。"""
user = User.query.get_or_404(user_id)
data = request.get_json()
if 'name' in data:
user.name = data['name']
if 'email' in data:
user.email = data['email']
db.session.commit()
return jsonify(user.to_dict())@users_bp.route('/int:user_id', methods=['DELETE'])
def delete_user(user_id):
"""删除用户。"""
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return '', 204app/init.py
app/init.py
def create_app():
app = Flask(name)
# Register blueprints
from app.routes.users import users_bp
from app.routes.products import products_bp
app.register_blueprint(users_bp, url_prefix='/api/users')
app.register_blueprint(products_bp, url_prefix='/api/products')
return appundefineddef create_app():
app = Flask(name)
# 注册蓝图
from app.routes.users import users_bp
from app.routes.products import products_bp
app.register_blueprint(users_bp, url_prefix='/api/users')
app.register_blueprint(products_bp, url_prefix='/api/products')
return appundefinedFlask-RESTful for APIs
Flask-RESTful 用于API开发
Basic REST API
基础REST API
python
undefinedpython
undefinedapp/api/resources.py
app/api/resources.py
from flask import request
from flask_restful import Resource, Api, reqparse, fields, marshal_with
from app.models.user import User
from app.extensions import db
from flask import request
from flask_restful import Resource, Api, reqparse, fields, marshal_with
from app.models.user import User
from app.extensions import db
Response serialization
响应序列化
user_fields = {
'id': fields.Integer,
'email': fields.String,
'name': fields.String,
'created_at': fields.DateTime(dt_format='iso8601')
}
class UserListResource(Resource):
"""User collection endpoint."""
@marshal_with(user_fields)
def get(self):
"""List all users."""
users = User.query.all()
return users
def post(self):
"""Create new user."""
parser = reqparse.RequestParser()
parser.add_argument('email', required=True, help='Email is required')
parser.add_argument('name', required=True, help='Name is required')
parser.add_argument('password', required=True, help='Password is required')
args = parser.parse_args()
user = User(email=args['email'], name=args['name'])
user.set_password(args['password'])
db.session.add(user)
db.session.commit()
return {'id': user.id, 'email': user.email, 'name': user.name}, 201class UserResource(Resource):
"""Single user endpoint."""
@marshal_with(user_fields)
def get(self, user_id):
"""Get user by ID."""
user = User.query.get_or_404(user_id)
return user
@marshal_with(user_fields)
def put(self, user_id):
"""Update user."""
user = User.query.get_or_404(user_id)
parser = reqparse.RequestParser()
parser.add_argument('name')
parser.add_argument('email')
args = parser.parse_args()
if args['name']:
user.name = args['name']
if args['email']:
user.email = args['email']
db.session.commit()
return user
def delete(self, user_id):
"""Delete user."""
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return '', 204user_fields = {
'id': fields.Integer,
'email': fields.String,
'name': fields.String,
'created_at': fields.DateTime(dt_format='iso8601')
}
class UserListResource(Resource):
"""用户集合端点。"""
@marshal_with(user_fields)
def get(self):
"""列出所有用户。"""
users = User.query.all()
return users
def post(self):
"""创建新用户。"""
parser = reqparse.RequestParser()
parser.add_argument('email', required=True, help='Email是必填项')
parser.add_argument('name', required=True, help='Name是必填项')
parser.add_argument('password', required=True, help='Password是必填项')
args = parser.parse_args()
user = User(email=args['email'], name=args['name'])
user.set_password(args['password'])
db.session.add(user)
db.session.commit()
return {'id': user.id, 'email': user.email, 'name': user.name}, 201class UserResource(Resource):
"""单个用户端点。"""
@marshal_with(user_fields)
def get(self, user_id):
"""根据ID获取用户。"""
user = User.query.get_or_404(user_id)
return user
@marshal_with(user_fields)
def put(self, user_id):
"""更新用户信息。"""
user = User.query.get_or_404(user_id)
parser = reqparse.RequestParser()
parser.add_argument('name')
parser.add_argument('email')
args = parser.parse_args()
if args['name']:
user.name = args['name']
if args['email']:
user.email = args['email']
db.session.commit()
return user
def delete(self, user_id):
"""删除用户。"""
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return '', 204app/init.py
app/init.py
from flask_restful import Api
def create_app():
app = Flask(name)
api = Api(app, prefix='/api/v1')
# Register resources
from app.api.resources import UserListResource, UserResource
api.add_resource(UserListResource, '/users')
api.add_resource(UserResource, '/users/<int:user_id>')
return appundefinedfrom flask_restful import Api
def create_app():
app = Flask(name)
api = Api(app, prefix='/api/v1')
# 注册资源
from app.api.resources import UserListResource, UserResource
api.add_resource(UserListResource, '/users')
api.add_resource(UserResource, '/users/<int:user_id>')
return appundefinedRequest Validation
请求验证
Marshmallow Schemas
Marshmallow 模式
python
undefinedpython
undefinedapp/schemas/user_schema.py
app/schemas/user_schema.py
from marshmallow import Schema, fields, validate, validates, ValidationError
class UserSchema(Schema):
"""User validation schema."""
id = fields.Int(dump_only=True)
email = fields.Email(required=True, validate=validate.Length(max=120))
name = fields.Str(required=True, validate=validate.Length(min=2, max=80))
password = fields.Str(
required=True,
load_only=True,
validate=validate.Length(min=8)
)
created_at = fields.DateTime(dump_only=True)
@validates('email')
def validate_email(self, value):
"""Check email uniqueness."""
from app.models.user import User
if User.query.filter_by(email=value).first():
raise ValidationError('Email already registered')class UserUpdateSchema(Schema):
"""User update schema (partial updates)."""
email = fields.Email(validate=validate.Length(max=120))
name = fields.Str(validate=validate.Length(min=2, max=80))
password = fields.Str(load_only=True, validate=validate.Length(min=8))from marshmallow import Schema, fields, validate, validates, ValidationError
class UserSchema(Schema):
"""用户验证模式。"""
id = fields.Int(dump_only=True)
email = fields.Email(required=True, validate=validate.Length(max=120))
name = fields.Str(required=True, validate=validate.Length(min=2, max=80))
password = fields.Str(
required=True,
load_only=True,
validate=validate.Length(min=8)
)
created_at = fields.DateTime(dump_only=True)
@validates('email')
def validate_email(self, value):
"""检查邮箱唯一性。"""
from app.models.user import User
if User.query.filter_by(email=value).first():
raise ValidationError('该邮箱已被注册')class UserUpdateSchema(Schema):
"""用户更新模式(支持部分更新)。"""
email = fields.Email(validate=validate.Length(max=120))
name = fields.Str(validate=validate.Length(min=2, max=80))
password = fields.Str(load_only=True, validate=validate.Length(min=8))Usage in routes
在路由中使用
from marshmallow import ValidationError
from app.schemas.user_schema import UserSchema, UserUpdateSchema
user_schema = UserSchema()
users_schema = UserSchema(many=True)
user_update_schema = UserUpdateSchema()
@users_bp.route('/', methods=['POST'])
def create_user():
"""Create user with validation."""
try:
# Validate and deserialize
data = user_schema.load(request.get_json())
except ValidationError as err:
return jsonify({"errors": err.messages}), 400
user = User(**data)
db.session.add(user)
db.session.commit()
# Serialize response
return user_schema.dump(user), 201@users_bp.route('/', methods=['GET'])
def list_users():
"""List users with serialization."""
users = User.query.all()
return jsonify(users_schema.dump(users))
undefinedfrom marshmallow import ValidationError
from app.schemas.user_schema import UserSchema, UserUpdateSchema
user_schema = UserSchema()
users_schema = UserSchema(many=True)
user_update_schema = UserUpdateSchema()
@users_bp.route('/', methods=['POST'])
def create_user():
"""通过验证创建用户。"""
try:
# 验证并反序列化
data = user_schema.load(request.get_json())
except ValidationError as err:
return jsonify({"errors": err.messages}), 400
user = User(**data)
db.session.add(user)
db.session.commit()
# 序列化响应
return user_schema.dump(user), 201@users_bp.route('/', methods=['GET'])
def list_users():
"""序列化并列出用户。"""
users = User.query.all()
return jsonify(users_schema.dump(users))
undefinedPydantic Validation
Pydantic 验证
python
undefinedpython
undefinedapp/schemas/user_schema_pydantic.py
app/schemas/user_schema_pydantic.py
from pydantic import BaseModel, EmailStr, Field, validator
from typing import Optional
from datetime import datetime
class UserCreate(BaseModel):
"""User creation schema."""
email: EmailStr
name: str = Field(..., min_length=2, max_length=80)
password: str = Field(..., min_length=8)
@validator('password')
def password_complexity(cls, v):
"""Validate password complexity."""
if not any(char.isdigit() for char in v):
raise ValueError('Password must contain at least one digit')
if not any(char.isupper() for char in v):
raise ValueError('Password must contain at least one uppercase letter')
return vclass UserUpdate(BaseModel):
"""User update schema."""
email: Optional[EmailStr] = None
name: Optional[str] = Field(None, min_length=2, max_length=80)class UserResponse(BaseModel):
"""User response schema."""
id: int
email: str
name: str
created_at: datetime
class Config:
orm_mode = Truefrom pydantic import BaseModel, EmailStr, Field, validator
from typing import Optional
from datetime import datetime
class UserCreate(BaseModel):
"""用户创建模式。"""
email: EmailStr
name: str = Field(..., min_length=2, max_length=80)
password: str = Field(..., min_length=8)
@validator('password')
def password_complexity(cls, v):
"""验证密码复杂度。"""
if not any(char.isdigit() for char in v):
raise ValueError('密码必须包含至少一个数字')
if not any(char.isupper() for char in v):
raise ValueError('密码必须包含至少一个大写字母')
return vclass UserUpdate(BaseModel):
"""用户更新模式。"""
email: Optional[EmailStr] = None
name: Optional[str] = Field(None, min_length=2, max_length=80)class UserResponse(BaseModel):
"""用户响应模式。"""
id: int
email: str
name: str
created_at: datetime
class Config:
orm_mode = TrueUsage
使用示例
from pydantic import ValidationError
@users_bp.route('/', methods=['POST'])
def create_user():
"""Create user with Pydantic validation."""
try:
user_data = UserCreate(**request.get_json())
except ValidationError as e:
return jsonify({"errors": e.errors()}), 400
user = User(
email=user_data.email,
name=user_data.name
)
user.set_password(user_data.password)
db.session.add(user)
db.session.commit()
return UserResponse.from_orm(user).dict(), 201undefinedfrom pydantic import ValidationError
@users_bp.route('/', methods=['POST'])
def create_user():
"""通过Pydantic验证创建用户。"""
try:
user_data = UserCreate(**request.get_json())
except ValidationError as e:
return jsonify({"errors": e.errors()}), 400
user = User(
email=user_data.email,
name=user_data.name
)
user.set_password(user_data.password)
db.session.add(user)
db.session.commit()
return UserResponse.from_orm(user).dict(), 201undefinedSQLAlchemy Integration
SQLAlchemy 集成
Models with Flask-SQLAlchemy
基于Flask-SQLAlchemy的模型
python
undefinedpython
undefinedapp/models/user.py
app/models/user.py
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from app.extensions import db
class User(db.Model):
"""User model."""
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False, index=True)
name = db.Column(db.String(80), nullable=False)
password_hash = db.Column(db.String(200), nullable=False)
is_active = db.Column(db.Boolean, default=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
posts = db.relationship('Post', backref='author', lazy='dynamic', cascade='all, delete-orphan')
def set_password(self, password):
"""Hash and set password."""
self.password_hash = generate_password_hash(password)
def check_password(self, password):
"""Verify password."""
return check_password_hash(self.password_hash, password)
def to_dict(self):
"""Serialize to dictionary."""
return {
'id': self.id,
'email': self.email,
'name': self.name,
'is_active': self.is_active,
'created_at': self.created_at.isoformat(),
'updated_at': self.updated_at.isoformat()
}
def __repr__(self):
return f'<User {self.email}>'from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from app.extensions import db
class User(db.Model):
"""用户模型。"""
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False, index=True)
name = db.Column(db.String(80), nullable=False)
password_hash = db.Column(db.String(200), nullable=False)
is_active = db.Column(db.Boolean, default=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# 关联关系
posts = db.relationship('Post', backref='author', lazy='dynamic', cascade='all, delete-orphan')
def set_password(self, password):
"""哈希并设置密码。"""
self.password_hash = generate_password_hash(password)
def check_password(self, password):
"""验证密码。"""
return check_password_hash(self.password_hash, password)
def to_dict(self):
"""序列化为字典。"""
return {
'id': self.id,
'email': self.email,
'name': self.name,
'is_active': self.is_active,
'created_at': self.created_at.isoformat(),
'updated_at': self.updated_at.isoformat()
}
def __repr__(self):
return f'<User {self.email}>'app/models/post.py
app/models/post.py
class Post(db.Model):
"""Post model."""
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def to_dict(self):
return {
'id': self.id,
'title': self.title,
'content': self.content,
'user_id': self.user_id,
'author': self.author.name,
'created_at': self.created_at.isoformat()
}class Post(db.Model):
"""文章模型。"""
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def to_dict(self):
return {
'id': self.id,
'title': self.title,
'content': self.content,
'user_id': self.user_id,
'author': self.author.name,
'created_at': self.created_at.isoformat()
}Database operations
数据库操作示例
@users_bp.route('/int:user_id/posts', methods=['GET'])
def get_user_posts(user_id):
"""Get user posts with pagination."""
user = User.query.get_or_404(user_id)
page = request.args.get('page', 1, type=int)
posts = user.posts.paginate(page=page, per_page=20)
return jsonify({
'posts': [p.to_dict() for p in posts.items],
'total': posts.total,
'page': posts.page
})undefined@users_bp.route('/int:user_id/posts', methods=['GET'])
def get_user_posts(user_id):
"""分页获取用户的文章。"""
user = User.query.get_or_404(user_id)
page = request.args.get('page', 1, type=int)
posts = user.posts.paginate(page=page, per_page=20)
return jsonify({
'posts': [p.to_dict() for p in posts.items],
'total': posts.total,
'page': posts.page
})undefinedDatabase Migrations
数据库迁移
bash
undefinedbash
undefinedInitialize migrations
初始化迁移
flask db init
flask db init
Create migration
创建迁移脚本
flask db migrate -m "Create users table"
flask db migrate -m "Create users table"
Apply migration
应用迁移
flask db upgrade
flask db upgrade
Rollback
回滚迁移
flask db downgrade
```pythonflask db downgrade
```pythonmigrations/versions/xxx_create_users.py (auto-generated)
migrations/versions/xxx_create_users.py(自动生成)
def upgrade():
op.create_table(
'users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('email', sa.String(length=120), nullable=False),
sa.Column('name', sa.String(length=80), nullable=False),
sa.Column('password_hash', sa.String(length=200), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email')
)
op.create_index('ix_users_email', 'users', ['email'])
def downgrade():
op.drop_index('ix_users_email', table_name='users')
op.drop_table('users')
undefineddef upgrade():
op.create_table(
'users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('email', sa.String(length=120), nullable=False),
sa.Column('name', sa.String(length=80), nullable=False),
sa.Column('password_hash', sa.String(length=200), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email')
)
op.create_index('ix_users_email', 'users', ['email'])
def downgrade():
op.drop_index('ix_users_email', table_name='users')
op.drop_table('users')
undefinedAuthentication
认证
Flask-Login Session-Based Auth
基于Flask-Login的会话认证
python
undefinedpython
undefinedapp/extensions.py
app/extensions.py
from flask_login import LoginManager
login_manager = LoginManager()
from flask_login import LoginManager
login_manager = LoginManager()
app/init.py
app/init.py
def create_app():
app = Flask(name)
login_manager.init_app(app)
login_manager.login_view = 'auth.login'
@login_manager.user_loader
def load_user(user_id):
from app.models.user import User
return User.query.get(int(user_id))
return appdef create_app():
app = Flask(name)
login_manager.init_app(app)
login_manager.login_view = 'auth.login'
@login_manager.user_loader
def load_user(user_id):
from app.models.user import User
return User.query.get(int(user_id))
return appapp/models/user.py
app/models/user.py
from flask_login import UserMixin
class User(UserMixin, db.Model):
# ... existing fields ...
def get_id(self):
return str(self.id)from flask_login import UserMixin
class User(UserMixin, db.Model):
# ... 现有字段 ...
def get_id(self):
return str(self.id)app/routes/auth.py
app/routes/auth.py
from flask import Blueprint, request, jsonify
from flask_login import login_user, logout_user, login_required, current_user
from app.models.user import User
auth_bp = Blueprint('auth', name)
@auth_bp.route('/login', methods=['POST'])
def login():
"""User login."""
data = request.get_json()
user = User.query.filter_by(email=data['email']).first()
if not user or not user.check_password(data['password']):
return jsonify({"error": "Invalid credentials"}), 401
login_user(user, remember=data.get('remember', False))
return jsonify(user.to_dict())@auth_bp.route('/logout', methods=['POST'])
@login_required
def logout():
"""User logout."""
logout_user()
return jsonify({"message": "Logged out successfully"})
@auth_bp.route('/me', methods=['GET'])
@login_required
def get_current_user():
"""Get current authenticated user."""
return jsonify(current_user.to_dict())
undefinedfrom flask import Blueprint, request, jsonify
from flask_login import login_user, logout_user, login_required, current_user
from app.models.user import User
auth_bp = Blueprint('auth', name)
@auth_bp.route('/login', methods=['POST'])
def login():
"""用户登录。"""
data = request.get_json()
user = User.query.filter_by(email=data['email']).first()
if not user or not user.check_password(data['password']):
return jsonify({"error": "无效的凭据"}), 401
login_user(user, remember=data.get('remember', False))
return jsonify(user.to_dict())@auth_bp.route('/logout', methods=['POST'])
@login_required
def logout():
"""用户登出。"""
logout_user()
return jsonify({"message": "成功登出"})
@auth_bp.route('/me', methods=['GET'])
@login_required
def get_current_user():
"""获取当前已认证用户。"""
return jsonify(current_user.to_dict())
undefinedJWT Authentication
JWT 认证
python
undefinedpython
undefinedapp/extensions.py
app/extensions.py
from flask_jwt_extended import JWTManager
jwt = JWTManager()
from flask_jwt_extended import JWTManager
jwt = JWTManager()
app/init.py
app/init.py
def create_app():
app = Flask(name)
app.config['JWT_SECRET_KEY'] = 'super-secret-key'
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)
app.config['JWT_REFRESH_TOKEN_EXPIRES'] = timedelta(days=30)
jwt.init_app(app)
return appdef create_app():
app = Flask(name)
app.config['JWT_SECRET_KEY'] = 'super-secret-key'
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)
app.config['JWT_REFRESH_TOKEN_EXPIRES'] = timedelta(days=30)
jwt.init_app(app)
return appapp/routes/auth.py
app/routes/auth.py
from flask_jwt_extended import (
create_access_token,
create_refresh_token,
jwt_required,
get_jwt_identity
)
@auth_bp.route('/login', methods=['POST'])
def login():
"""Login and return JWT tokens."""
data = request.get_json()
user = User.query.filter_by(email=data['email']).first()
if not user or not user.check_password(data['password']):
return jsonify({"error": "Invalid credentials"}), 401
access_token = create_access_token(identity=user.id)
refresh_token = create_refresh_token(identity=user.id)
return jsonify({
"access_token": access_token,
"refresh_token": refresh_token,
"user": user.to_dict()
})@auth_bp.route('/refresh', methods=['POST'])
@jwt_required(refresh=True)
def refresh():
"""Refresh access token."""
current_user_id = get_jwt_identity()
access_token = create_access_token(identity=current_user_id)
return jsonify({"access_token": access_token})@auth_bp.route('/me', methods=['GET'])
@jwt_required()
def get_current_user():
"""Get current user from JWT."""
current_user_id = get_jwt_identity()
user = User.query.get_or_404(current_user_id)
return jsonify(user.to_dict())from flask_jwt_extended import (
create_access_token,
create_refresh_token,
jwt_required,
get_jwt_identity
)
@auth_bp.route('/login', methods=['POST'])
def login():
"""登录并返回JWT令牌。"""
data = request.get_json()
user = User.query.filter_by(email=data['email']).first()
if not user or not user.check_password(data['password']):
return jsonify({"error": "无效的凭据"}), 401
access_token = create_access_token(identity=user.id)
refresh_token = create_refresh_token(identity=user.id)
return jsonify({
"access_token": access_token,
"refresh_token": refresh_token,
"user": user.to_dict()
})@auth_bp.route('/refresh', methods=['POST'])
@jwt_required(refresh=True)
def refresh():
"""刷新访问令牌。"""
current_user_id = get_jwt_identity()
access_token = create_access_token(identity=current_user_id)
return jsonify({"access_token": access_token})@auth_bp.route('/me', methods=['GET'])
@jwt_required()
def get_current_user():
"""从JWT获取当前用户。"""
current_user_id = get_jwt_identity()
user = User.query.get_or_404(current_user_id)
return jsonify(user.to_dict())Protected route example
受保护路由示例
@users_bp.route('/int:user_id', methods=['DELETE'])
@jwt_required()
def delete_user(user_id):
"""Delete user (authenticated)."""
current_user_id = get_jwt_identity()
# Authorization check
if current_user_id != user_id:
return jsonify({"error": "Unauthorized"}), 403
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return '', 204undefined@users_bp.route('/int:user_id', methods=['DELETE'])
@jwt_required()
def delete_user(user_id):
"""删除用户(需认证)。"""
current_user_id = get_jwt_identity()
# 权限检查
if current_user_id != user_id:
return jsonify({"error": "无权限操作"}), 403
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return '', 204undefinedConfiguration Management
配置管理
Environment-Based Configuration
基于环境的配置
python
undefinedpython
undefinedapp/config.py
app/config.py
import os
from datetime import timedelta
class Config:
"""Base configuration."""
# Flask
SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')
# SQLAlchemy
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL', 'sqlite:///app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = False
# JWT
JWT_SECRET_KEY = os.getenv('JWT_SECRET_KEY', 'jwt-secret-change-in-production')
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=1)
JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30)
# CORS
CORS_ORIGINS = os.getenv('CORS_ORIGINS', '*').split(',')
# Pagination
ITEMS_PER_PAGE = 20
MAX_ITEMS_PER_PAGE = 100
# Logging
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
LOG_FILE = os.getenv('LOG_FILE', 'app.log')class DevelopmentConfig(Config):
"""Development configuration."""
DEBUG = True
TESTING = False
SQLALCHEMY_ECHO = True
class TestingConfig(Config):
"""Testing configuration."""
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
WTF_CSRF_ENABLED = False
class ProductionConfig(Config):
"""Production configuration."""
DEBUG = False
TESTING = False
# Strict security requirements
SECRET_KEY = os.getenv('SECRET_KEY')
JWT_SECRET_KEY = os.getenv('JWT_SECRET_KEY')
# Production database (PostgreSQL recommended)
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL')
@classmethod
def init_app(cls, app):
"""Production-specific initialization."""
# Log to syslog or external service
import logging
from logging.handlers import SysLogHandler
syslog_handler = SysLogHandler()
syslog_handler.setLevel(logging.WARNING)
app.logger.addHandler(syslog_handler)import os
from datetime import timedelta
class Config:
"""基础配置。"""
# Flask
SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')
# SQLAlchemy
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL', 'sqlite:///app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = False
# JWT
JWT_SECRET_KEY = os.getenv('JWT_SECRET_KEY', 'jwt-secret-change-in-production')
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=1)
JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30)
# CORS
CORS_ORIGINS = os.getenv('CORS_ORIGINS', '*').split(',')
# 分页
ITEMS_PER_PAGE = 20
MAX_ITEMS_PER_PAGE = 100
# 日志
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
LOG_FILE = os.getenv('LOG_FILE', 'app.log')class DevelopmentConfig(Config):
"""开发环境配置。"""
DEBUG = True
TESTING = False
SQLALCHEMY_ECHO = True
class TestingConfig(Config):
"""测试环境配置。"""
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
WTF_CSRF_ENABLED = False
class ProductionConfig(Config):
"""生产环境配置。"""
DEBUG = False
TESTING = False
# 严格的安全要求
SECRET_KEY = os.getenv('SECRET_KEY')
JWT_SECRET_KEY = os.getenv('JWT_SECRET_KEY')
# 生产数据库(推荐PostgreSQL)
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL')
@classmethod
def init_app(cls, app):
"""生产环境专属初始化。"""
# 日志输出到syslog或外部服务
import logging
from logging.handlers import SysLogHandler
syslog_handler = SysLogHandler()
syslog_handler.setLevel(logging.WARNING)
app.logger.addHandler(syslog_handler)Configuration factory
配置工厂
config_by_name = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
def get_config(env_name='default'):
"""Get configuration by environment name."""
return config_by_name.get(env_name, DevelopmentConfig)
config_by_name = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
def get_config(env_name='default'):
"""根据环境名称获取配置。"""
return config_by_name.get(env_name, DevelopmentConfig)
app/init.py
app/init.py
def create_app(config_name='default'):
app = Flask(name)
app.config.from_object(get_config(config_name))
return appundefineddef create_app(config_name='default'):
app = Flask(name)
app.config.from_object(get_config(config_name))
return appundefined.env File Support
.env 文件支持
bash
undefinedbash
undefined.env
.env
FLASK_APP=run.py
FLASK_ENV=development
SECRET_KEY=your-secret-key-here
JWT_SECRET_KEY=your-jwt-secret-here
DATABASE_URL=postgresql://user:password@localhost/dbname
CORS_ORIGINS=http://localhost:3000,http://localhost:8000
```pythonFLASK_APP=run.py
FLASK_ENV=development
SECRET_KEY=your-secret-key-here
JWT_SECRET_KEY=your-jwt-secret-here
DATABASE_URL=postgresql://user:password@localhost/dbname
CORS_ORIGINS=http://localhost:3000,http://localhost:8000
```pythonLoad environment variables
加载环境变量
from dotenv import load_dotenv
load_dotenv()
from dotenv import load_dotenv
load_dotenv()
app/init.py
app/init.py
import os
from dotenv import load_dotenv
def create_app():
load_dotenv()
config_name = os.getenv('FLASK_ENV', 'development')
app = Flask(__name__)
app.config.from_object(get_config(config_name))
return appundefinedimport os
from dotenv import load_dotenv
def create_app():
load_dotenv()
config_name = os.getenv('FLASK_ENV', 'development')
app = Flask(__name__)
app.config.from_object(get_config(config_name))
return appundefinedError Handling and Logging
错误处理与日志
Global Error Handlers
全局错误处理器
python
undefinedpython
undefinedapp/errors/handlers.py
app/errors/handlers.py
from flask import jsonify
from werkzeug.exceptions import HTTPException
from sqlalchemy.exc import SQLAlchemyError
def register_error_handlers(app):
"""Register global error handlers."""
@app.errorhandler(404)
def not_found_error(error):
return jsonify({
"error": "Not Found",
"message": "The requested resource was not found"
}), 404
@app.errorhandler(400)
def bad_request_error(error):
return jsonify({
"error": "Bad Request",
"message": str(error.description) if hasattr(error, 'description') else "Invalid request"
}), 400
@app.errorhandler(401)
def unauthorized_error(error):
return jsonify({
"error": "Unauthorized",
"message": "Authentication required"
}), 401
@app.errorhandler(403)
def forbidden_error(error):
return jsonify({
"error": "Forbidden",
"message": "You don't have permission to access this resource"
}), 403
@app.errorhandler(500)
def internal_error(error):
app.logger.error(f'Internal server error: {error}')
db.session.rollback()
return jsonify({
"error": "Internal Server Error",
"message": "An unexpected error occurred"
}), 500
@app.errorhandler(HTTPException)
def handle_http_exception(error):
"""Handle all HTTP exceptions."""
return jsonify({
"error": error.name,
"message": error.description
}), error.code
@app.errorhandler(SQLAlchemyError)
def handle_db_error(error):
"""Handle database errors."""
app.logger.error(f'Database error: {error}')
db.session.rollback()
return jsonify({
"error": "Database Error",
"message": "A database error occurred"
}), 500from flask import jsonify
from werkzeug.exceptions import HTTPException
from sqlalchemy.exc import SQLAlchemyError
def register_error_handlers(app):
"""注册全局错误处理器。"""
@app.errorhandler(404)
def not_found_error(error):
return jsonify({
"error": "未找到",
"message": "请求的资源不存在"
}), 404
@app.errorhandler(400)
def bad_request_error(error):
return jsonify({
"error": "错误请求",
"message": str(error.description) if hasattr(error, 'description') else "无效请求"
}), 400
@app.errorhandler(401)
def unauthorized_error(error):
return jsonify({
"error": "未授权",
"message": "需要认证"
}), 401
@app.errorhandler(403)
def forbidden_error(error):
return jsonify({
"error": "禁止访问",
"message": "你没有权限访问该资源"
}), 403
@app.errorhandler(500)
def internal_error(error):
app.logger.error(f'内部服务器错误: {error}')
db.session.rollback()
return jsonify({
"error": "内部服务器错误",
"message": "发生了意外错误"
}), 500
@app.errorhandler(HTTPException)
def handle_http_exception(error):
"""处理所有HTTP异常。"""
return jsonify({
"error": error.name,
"message": error.description
}), error.code
@app.errorhandler(SQLAlchemyError)
def handle_db_error(error):
"""处理数据库错误。"""
app.logger.error(f'数据库错误: {error}')
db.session.rollback()
return jsonify({
"error": "数据库错误",
"message": "发生了数据库错误"
}), 500app/init.py
app/init.py
def create_app():
app = Flask(name)
from app.errors.handlers import register_error_handlers
register_error_handlers(app)
return appundefineddef create_app():
app = Flask(name)
from app.errors.handlers import register_error_handlers
register_error_handlers(app)
return appundefinedLogging Configuration
日志配置
python
undefinedpython
undefinedapp/logging_config.py
app/logging_config.py
import logging
from logging.handlers import RotatingFileHandler
import os
def configure_logging(app):
"""Configure application logging."""
if not app.debug and not app.testing:
# Create logs directory if it doesn't exist
if not os.path.exists('logs'):
os.mkdir('logs')
# File handler with rotation
file_handler = RotatingFileHandler(
'logs/flask_app.log',
maxBytes=10240000, # 10MB
backupCount=10
)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s '
'[in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('Flask application startup')import logging
from logging.handlers import RotatingFileHandler
import os
def configure_logging(app):
"""配置应用日志。"""
if not app.debug and not app.testing:
# 如果日志目录不存在则创建
if not os.path.exists('logs'):
os.mkdir('logs')
# 带轮转的文件处理器
file_handler = RotatingFileHandler(
'logs/flask_app.log',
maxBytes=10240000, # 10MB
backupCount=10
)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s '
'[in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('Flask应用启动')app/init.py
app/init.py
def create_app():
app = Flask(name)
from app.logging_config import configure_logging
configure_logging(app)
return appdef create_app():
app = Flask(name)
from app.logging_config import configure_logging
configure_logging(app)
return appUsage in routes
在路由中使用
@users_bp.route('/', methods=['POST'])
def create_user():
current_app.logger.info(f'Creating new user: {request.get_json()}')
# ... create user ...
current_app.logger.info(f'User created successfully: {user.id}')
undefined@users_bp.route('/', methods=['POST'])
def create_user():
current_app.logger.info(f'创建新用户: {request.get_json()}')
# ... 创建用户 ...
current_app.logger.info(f'用户创建成功: {user.id}')
undefinedTesting with pytest
使用pytest进行测试
Test Configuration
测试配置
python
undefinedpython
undefinedconftest.py
conftest.py
import pytest
from app import create_app
from app.extensions import db
from app.models.user import User
@pytest.fixture(scope='session')
def app():
"""Create application for testing."""
app = create_app('testing')
with app.app_context():
db.create_all()
yield app
db.drop_all()@pytest.fixture
def client(app):
"""Flask test client."""
return app.test_client()
@pytest.fixture
def runner(app):
"""Flask CLI test runner."""
return app.test_cli_runner()
@pytest.fixture
def db_session(app):
"""Database session for testing."""
with app.app_context():
db.session.begin_nested()
yield db.session
db.session.rollback()
@pytest.fixture
def sample_user(db_session):
"""Create sample user."""
user = User(email='test@example.com', name='Test User')
user.set_password('password123')
db_session.add(user)
db_session.commit()
return user
@pytest.fixture
def auth_headers(client, sample_user):
"""Get JWT authentication headers."""
response = client.post('/auth/login', json={
'email': 'test@example.com',
'password': 'password123'
})
token = response.get_json()['access_token']
return {'Authorization': f'Bearer {token}'}
undefinedimport pytest
from app import create_app
from app.extensions import db
from app.models.user import User
@pytest.fixture(scope='session')
def app():
"""创建测试用应用。"""
app = create_app('testing')
with app.app_context():
db.create_all()
yield app
db.drop_all()@pytest.fixture
def client(app):
"""Flask测试客户端。"""
return app.test_client()
@pytest.fixture
def runner(app):
"""Flask CLI测试运行器。"""
return app.test_cli_runner()
@pytest.fixture
def db_session(app):
"""测试用数据库会话。"""
with app.app_context():
db.session.begin_nested()
yield db.session
db.session.rollback()
@pytest.fixture
def sample_user(db_session):
"""创建示例用户。"""
user = User(email='test@example.com', name='Test User')
user.set_password('password123')
db_session.add(user)
db_session.commit()
return user
@pytest.fixture
def auth_headers(client, sample_user):
"""获取JWT认证头。"""
response = client.post('/auth/login', json={
'email': 'test@example.com',
'password': 'password123'
})
token = response.get_json()['access_token']
return {'Authorization': f'Bearer {token}'}
undefinedAPI Testing
API测试
python
undefinedpython
undefinedtests/test_users.py
tests/test_users.py
import pytest
from app.models.user import User
def test_create_user(client):
"""Test user creation endpoint."""
response = client.post('/api/users', json={
'email': 'new@example.com',
'name': 'New User',
'password': 'password123'
})
assert response.status_code == 201
data = response.get_json()
assert data['email'] == 'new@example.com'
assert data['name'] == 'New User'
assert 'id' in datadef test_get_user(client, sample_user):
"""Test get user endpoint."""
response = client.get(f'/api/users/{sample_user.id}')
assert response.status_code == 200
data = response.get_json()
assert data['id'] == sample_user.id
assert data['email'] == sample_user.emaildef test_list_users(client, sample_user):
"""Test list users endpoint."""
response = client.get('/api/users')
assert response.status_code == 200
data = response.get_json()
assert 'users' in data
assert len(data['users']) > 0def test_update_user(client, sample_user, auth_headers):
"""Test user update endpoint."""
response = client.put(
f'/api/users/{sample_user.id}',
json={'name': 'Updated Name'},
headers=auth_headers
)
assert response.status_code == 200
data = response.get_json()
assert data['name'] == 'Updated Name'def test_delete_user(client, sample_user, auth_headers):
"""Test user deletion endpoint."""
response = client.delete(
f'/api/users/{sample_user.id}',
headers=auth_headers
)
assert response.status_code == 204
assert User.query.get(sample_user.id) is Nonedef test_authentication_required(client, sample_user):
"""Test that protected endpoints require authentication."""
response = client.delete(f'/api/users/{sample_user.id}')
assert response.status_code == 401
undefinedimport pytest
from app.models.user import User
def test_create_user(client):
"""测试用户创建端点。"""
response = client.post('/api/users', json={
'email': 'new@example.com',
'name': 'New User',
'password': 'password123'
})
assert response.status_code == 201
data = response.get_json()
assert data['email'] == 'new@example.com'
assert data['name'] == 'New User'
assert 'id' in datadef test_get_user(client, sample_user):
"""测试获取用户端点。"""
response = client.get(f'/api/users/{sample_user.id}')
assert response.status_code == 200
data = response.get_json()
assert data['id'] == sample_user.id
assert data['email'] == sample_user.emaildef test_list_users(client, sample_user):
"""测试列出用户端点。"""
response = client.get('/api/users')
assert response.status_code == 200
data = response.get_json()
assert 'users' in data
assert len(data['users']) > 0def test_update_user(client, sample_user, auth_headers):
"""测试用户更新端点。"""
response = client.put(
f'/api/users/{sample_user.id}',
json={'name': 'Updated Name'},
headers=auth_headers
)
assert response.status_code == 200
data = response.get_json()
assert data['name'] == 'Updated Name'def test_delete_user(client, sample_user, auth_headers):
"""测试用户删除端点。"""
response = client.delete(
f'/api/users/{sample_user.id}',
headers=auth_headers
)
assert response.status_code == 204
assert User.query.get(sample_user.id) is Nonedef test_authentication_required(client, sample_user):
"""测试受保护端点需要认证。"""
response = client.delete(f'/api/users/{sample_user.id}')
assert response.status_code == 401
undefinedDeployment
部署
Production with Gunicorn
使用Gunicorn部署到生产环境
bash
undefinedbash
undefinedInstall Gunicorn
安装Gunicorn
pip install gunicorn
pip install gunicorn
Run with Gunicorn
使用Gunicorn运行
gunicorn --workers 4 --bind 0.0.0.0:8000 "app:create_app()"
gunicorn --workers 4 --bind 0.0.0.0:8000 "app:create_app()"
With environment variable
带环境变量
gunicorn --workers 4 --bind 0.0.0.0:8000 --env FLASK_ENV=production "app:create_app()"
**gunicorn.conf.py:**
```python
import multiprocessinggunicorn --workers 4 --bind 0.0.0.0:8000 --env FLASK_ENV=production "app:create_app()"
**gunicorn.conf.py:**
```python
import multiprocessingServer socket
服务器套接字
bind = '0.0.0.0:8000'
backlog = 2048
bind = '0.0.0.0:8000'
backlog = 2048
Worker processes
工作进程
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'sync'
worker_connections = 1000
timeout = 30
keepalive = 2
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'sync'
worker_connections = 1000
timeout = 30
keepalive = 2
Logging
日志
accesslog = 'logs/access.log'
errorlog = 'logs/error.log'
loglevel = 'info'
accesslog = 'logs/access.log'
errorlog = 'logs/error.log'
loglevel = 'info'
Process naming
进程命名
proc_name = 'flask-app'
proc_name = 'flask-app'
Server mechanics
服务器机制
daemon = False
pidfile = 'gunicorn.pid'
undefineddaemon = False
pidfile = 'gunicorn.pid'
undefinedDocker Deployment
Docker部署
Dockerfile:
dockerfile
FROM python:3.11-slim
WORKDIR /appDockerfile:
dockerfile
FROM python:3.11-slim
WORKDIR /appInstall dependencies
安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
Copy application
复制应用
COPY . .
COPY . .
Create non-root user
创建非root用户
RUN useradd -m -u 1000 flask && chown -R flask:flask /app
USER flask
RUN useradd -m -u 1000 flask && chown -R flask:flask /app
USER flask
Run with Gunicorn
使用Gunicorn运行
CMD ["gunicorn", "--config", "gunicorn.conf.py", "app:create_app()"]
**docker-compose.yml:**
```yaml
version: '3.8'
services:
app:
build: .
ports:
- "8000:8000"
environment:
- FLASK_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/flask_db
- SECRET_KEY=${SECRET_KEY}
- JWT_SECRET_KEY=${JWT_SECRET_KEY}
depends_on:
- db
- redis
volumes:
- ./logs:/app/logs
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=flask_db
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- app
volumes:
postgres_data:
redis_data:CMD ["gunicorn", "--config", "gunicorn.conf.py", "app:create_app()"]
**docker-compose.yml:**
```yaml
version: '3.8'
services:
app:
build: .
ports:
- "8000:8000"
environment:
- FLASK_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/flask_db
- SECRET_KEY=${SECRET_KEY}
- JWT_SECRET_KEY=${JWT_SECRET_KEY}
depends_on:
- db
- redis
volumes:
- ./logs:/app/logs
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=flask_db
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- app
volumes:
postgres_data:
redis_data:Microservices Patterns
微服务模式
Service Communication
服务通信
python
undefinedpython
undefinedapp/services/external_api.py
app/services/external_api.py
import requests
from flask import current_app
class ExternalAPIService:
"""External API client service."""
def __init__(self, base_url, timeout=10):
self.base_url = base_url
self.timeout = timeout
def get(self, endpoint, **kwargs):
"""GET request to external API."""
url = f"{self.base_url}/{endpoint}"
try:
response = requests.get(url, timeout=self.timeout, **kwargs)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
current_app.logger.error(f'External API error: {e}')
raise
def post(self, endpoint, data, **kwargs):
"""POST request to external API."""
url = f"{self.base_url}/{endpoint}"
try:
response = requests.post(url, json=data, timeout=self.timeout, **kwargs)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
current_app.logger.error(f'External API error: {e}')
raiseimport requests
from flask import current_app
class ExternalAPIService:
"""外部API客户端服务。"""
def __init__(self, base_url, timeout=10):
self.base_url = base_url
self.timeout = timeout
def get(self, endpoint, **kwargs):
"""向外部API发送GET请求。"""
url = f"{self.base_url}/{endpoint}"
try:
response = requests.get(url, timeout=self.timeout, **kwargs)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
current_app.logger.error(f'外部API错误: {e}')
raise
def post(self, endpoint, data, **kwargs):
"""向外部API发送POST请求。"""
url = f"{self.base_url}/{endpoint}"
try:
response = requests.post(url, json=data, timeout=self.timeout, **kwargs)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
current_app.logger.error(f'外部API错误: {e}')
raiseUsage
使用示例
api_service = ExternalAPIService('https://api.example.com')
@app.route('/proxy/data')
def proxy_data():
"""Proxy request to external service."""
try:
data = api_service.get('data')
return jsonify(data)
except Exception as e:
return jsonify({"error": "External service unavailable"}), 503
undefinedapi_service = ExternalAPIService('https://api.example.com')
@app.route('/proxy/data')
def proxy_data():
"""代理请求到外部服务。"""
try:
data = api_service.get('data')
return jsonify(data)
except Exception as e:
return jsonify({"error": "外部服务不可用"}), 503
undefinedHealth Checks
健康检查
python
undefinedpython
undefinedapp/routes/health.py
app/routes/health.py
from flask import Blueprint, jsonify
from app.extensions import db
from sqlalchemy import text
health_bp = Blueprint('health', name)
@health_bp.route('/health')
def health_check():
"""Basic health check."""
return jsonify({"status": "healthy"}), 200
@health_bp.route('/health/ready')
def readiness_check():
"""Readiness check with dependencies."""
checks = {
"database": False,
"status": "unhealthy"
}
# Check database
try:
db.session.execute(text('SELECT 1'))
checks["database"] = True
except Exception as e:
current_app.logger.error(f'Database health check failed: {e}')
# Overall status
checks["status"] = "healthy" if all([checks["database"]]) else "unhealthy"
status_code = 200 if checks["status"] == "healthy" else 503
return jsonify(checks), status_codeundefinedfrom flask import Blueprint, jsonify
from app.extensions import db
from sqlalchemy import text
health_bp = Blueprint('health', name)
@health_bp.route('/health')
def health_check():
"""基础健康检查。"""
return jsonify({"status": "healthy"}), 200
@health_bp.route('/health/ready')
def readiness_check():
"""包含依赖检查的就绪性检查。"""
checks = {
"database": False,
"status": "unhealthy"
}
# 检查数据库
try:
db.session.execute(text('SELECT 1'))
checks["database"] = True
except Exception as e:
current_app.logger.error(f'数据库健康检查失败: {e}')
# 整体状态
checks["status"] = "healthy" if all([checks["database"]]) else "unhealthy"
status_code = 200 if checks["status"] == "healthy" else 503
return jsonify(checks), status_codeundefinedBest Practices
最佳实践
1. Use Application Factory Pattern
1. 使用应用工厂模式
python
undefinedpython
undefined✅ GOOD: Application factory
✅ 推荐:应用工厂
def create_app(config_name='default'):
app = Flask(name)
app.config.from_object(get_config(config_name))
return app
def create_app(config_name='default'):
app = Flask(name)
app.config.from_object(get_config(config_name))
return app
❌ BAD: Global app instance
❌ 不推荐:全局应用实例
app = Flask(name)
undefinedapp = Flask(name)
undefined2. Use Blueprints for Modularity
2. 使用蓝图实现模块化
python
undefinedpython
undefined✅ GOOD: Organized blueprints
✅ 推荐:结构化蓝图
from app.routes.users import users_bp
from app.routes.products import products_bp
app.register_blueprint(users_bp, url_prefix='/api/users')
app.register_blueprint(products_bp, url_prefix='/api/products')
from app.routes.users import users_bp
from app.routes.products import products_bp
app.register_blueprint(users_bp, url_prefix='/api/users')
app.register_blueprint(products_bp, url_prefix='/api/products')
❌ BAD: All routes in single file
❌ 不推荐:所有路由放在单个文件中
undefinedundefined3. Validate All Input
3. 验证所有输入
python
undefinedpython
undefined✅ GOOD: Validation with Marshmallow/Pydantic
✅ 推荐:使用Marshmallow/Pydantic验证
try:
data = user_schema.load(request.get_json())
except ValidationError as e:
return jsonify({"errors": e.messages}), 400
try:
data = user_schema.load(request.get_json())
except ValidationError as e:
return jsonify({"errors": e.messages}), 400
❌ BAD: No validation
❌ 不推荐:无验证
data = request.get_json()
user = User(**data) # Unsafe!
undefineddata = request.get_json()
user = User(**data) # 不安全!
undefined4. Use Environment Variables for Secrets
4. 使用环境变量存储敏感信息
python
undefinedpython
undefined✅ GOOD: Environment variables
✅ 推荐:环境变量
SECRET_KEY = os.getenv('SECRET_KEY')
SECRET_KEY = os.getenv('SECRET_KEY')
❌ BAD: Hardcoded secrets
❌ 不推荐:硬编码敏感信息
SECRET_KEY = 'hardcoded-secret-key'
undefinedSECRET_KEY = 'hardcoded-secret-key'
undefined5. Implement Proper Error Handling
5. 实现完善的错误处理
python
undefinedpython
undefined✅ GOOD: Global error handlers
✅ 推荐:全局错误处理器
@app.errorhandler(Exception)
def handle_error(error):
app.logger.error(f'Error: {error}')
return jsonify({"error": "Internal server error"}), 500
@app.errorhandler(Exception)
def handle_error(error):
app.logger.error(f'错误: {error}')
return jsonify({"error": "内部服务器错误"}), 500
❌ BAD: Unhandled exceptions
❌ 不推荐:未处理异常
undefinedundefinedResources
资源
- Official Documentation: https://flask.palletsprojects.com/
- Flask-RESTful: https://flask-restful.readthedocs.io/
- Flask-SQLAlchemy: https://flask-sqlalchemy.palletsprojects.com/
- Flask-JWT-Extended: https://flask-jwt-extended.readthedocs.io/
- Marshmallow: https://marshmallow.readthedocs.io/
- Pydantic: https://pydantic-docs.helpmanual.io/
- 官方文档: https://flask.palletsprojects.com/
- Flask-RESTful: https://flask-restful.readthedocs.io/
- Flask-SQLAlchemy: https://flask-sqlalchemy.palletsprojects.com/
- Flask-JWT-Extended: https://flask-jwt-extended.readthedocs.io/
- Marshmallow: https://marshmallow.readthedocs.io/
- Pydantic: https://pydantic-docs.helpmanual.io/
Related Skills
相关技能
When using Flask, consider these complementary skills:
- pytest: Testing Flask applications with fixtures and test client
- sqlalchemy: Database ORM patterns with Flask-SQLAlchemy integration
- fastapi-local-dev: Modern async alternative for high-performance APIs
- django: Batteries-included framework with built-in admin and ORM
使用Flask时,可考虑以下互补技能:
- pytest: 使用fixture和测试客户端测试Flask应用
- sqlalchemy: 结合Flask-SQLAlchemy的数据库ORM模式
- fastapi-local-dev: 用于高性能API的现代异步替代方案
- django: 自带管理后台和ORM的全功能框架
Quick Flask Testing Patterns (Inlined for Standalone Use)
快速Flask测试模式(独立使用)
python
undefinedpython
undefinedTesting Flask with pytest
使用pytest测试Flask
import pytest
from flask import Flask
from app import create_app, db
@pytest.fixture
def app():
"""Create and configure test app"""
app = create_app('testing')
with app.app_context():
db.create_all()
yield app
db.drop_all()
@pytest.fixture
def client(app):
"""Test client for making requests"""
return app.test_client()
@pytest.fixture
def runner(app):
"""CLI test runner"""
return app.test_cli_runner()
import pytest
from flask import Flask
from app import create_app, db
@pytest.fixture
def app():
"""创建并配置测试应用"""
app = create_app('testing')
with app.app_context():
db.create_all()
yield app
db.drop_all()
@pytest.fixture
def client(app):
"""用于发送请求的测试客户端"""
return app.test_client()
@pytest.fixture
def runner(app):
"""CLI测试运行器"""
return app.test_cli_runner()
Test routes
测试路由
def test_home_page(client):
response = client.get('/')
assert response.status_code == 200
assert b'Welcome' in response.data
def test_api_endpoint(client):
response = client.post('/api/users', json={
'username': 'alice',
'email': 'alice@example.com'
})
assert response.status_code == 201
data = response.get_json()
assert data['username'] == 'alice'
def test_authentication(client):
# Login
response = client.post('/login', data={
'username': 'alice',
'password': 'secret123'
})
assert response.status_code == 302 # Redirect after login
# Access protected route
response = client.get('/dashboard')
assert response.status_code == 200def test_home_page(client):
response = client.get('/')
assert response.status_code == 200
assert b'Welcome' in response.data
def test_api_endpoint(client):
response = client.post('/api/users', json={
'username': 'alice',
'email': 'alice@example.com'
})
assert response.status_code == 201
data = response.get_json()
assert data['username'] == 'alice'
def test_authentication(client):
# 登录
response = client.post('/login', data={
'username': 'alice',
'password': 'secret123'
})
assert response.status_code == 302 # 登录后重定向
# 访问受保护路由
response = client.get('/dashboard')
assert response.status_code == 200Test with database
数据库测试
def test_user_creation(app):
with app.app_context():
user = User(username='bob', email='bob@example.com')
db.session.add(user)
db.session.commit()
found = User.query.filter_by(username='bob').first()
assert found is not None
assert found.email == 'bob@example.com'def test_user_creation(app):
with app.app_context():
user = User(username='bob', email='bob@example.com')
db.session.add(user)
db.session.commit()
found = User.query.filter_by(username='bob').first()
assert found is not None
assert found.email == 'bob@example.com'Test error handling
错误处理测试
def test_404_error(client):
response = client.get('/nonexistent')
assert response.status_code == 404
assert b'Not Found' in response.data
undefineddef test_404_error(client):
response = client.get('/nonexistent')
assert response.status_code == 404
assert b'Not Found' in response.data
undefinedQuick SQLAlchemy Integration (Inlined for Standalone Use)
快速SQLAlchemy集成(独立使用)
python
undefinedpython
undefinedFlask-SQLAlchemy setup
Flask-SQLAlchemy设置
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
pass
db = SQLAlchemy(model_class=Base)
def create_app():
app = Flask(name)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:pass@localhost/db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
with app.app_context():
db.create_all()
return appfrom flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
pass
db = SQLAlchemy(model_class=Base)
def create_app():
app = Flask(name)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:pass@localhost/db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
with app.app_context():
db.create_all()
return appDefine models
定义模型
class User(db.Model):
tablename = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(100), unique=True, nullable=False)
posts = db.relationship('Post', backref='author', lazy='dynamic')class Post(db.Model):
tablename = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)class User(db.Model):
tablename = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(100), unique=True, nullable=False)
posts = db.relationship('Post', backref='author', lazy='dynamic')class Post(db.Model):
tablename = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)Query patterns
查询模式
@app.route('/users/int:user_id')
def get_user(user_id):
user = db.session.get(User, user_id)
if not user:
abort(404)
return jsonify({
'id': user.id,
'username': user.username,
'email': user.email,
'posts': [{'title': p.title} for p in user.posts]
})
@app.route('/users', methods=['POST'])
def create_user():
data = request.get_json()
user = User(username=data['username'], email=data['email'])
db.session.add(user)
db.session.commit()
return jsonify({'id': user.id}), 201
@app.route('/users/int:user_id')
def get_user(user_id):
user = db.session.get(User, user_id)
if not user:
abort(404)
return jsonify({
'id': user.id,
'username': user.username,
'email': user.email,
'posts': [{'title': p.title} for p in user.posts]
})
@app.route('/users', methods=['POST'])
def create_user():
data = request.get_json()
user = User(username=data['username'], email=data['email'])
db.session.add(user)
db.session.commit()
return jsonify({'id': user.id}), 201
Transaction handling
事务处理
from sqlalchemy.exc import IntegrityError
@app.route('/transfer', methods=['POST'])
def transfer_funds():
try:
# All operations in single transaction
sender = User.query.get_or_404(request.json['sender_id'])
receiver = User.query.get_or_404(request.json['receiver_id'])
amount = request.json['amount']
sender.balance -= amount
receiver.balance += amount
db.session.commit()
return jsonify({'status': 'success'})
except IntegrityError:
db.session.rollback()
return jsonify({'error': 'Transaction failed'}), 400undefinedfrom sqlalchemy.exc import IntegrityError
@app.route('/transfer', methods=['POST'])
def transfer_funds():
try:
# 所有操作在单个事务中
sender = User.query.get_or_404(request.json['sender_id'])
receiver = User.query.get_or_404(request.json['receiver_id'])
amount = request.json['amount']
sender.balance -= amount
receiver.balance += amount
db.session.commit()
return jsonify({'status': 'success'})
except IntegrityError:
db.session.rollback()
return jsonify({'error': '交易失败'}), 400undefinedQuick FastAPI Comparison (Inlined for Standalone Use)
快速FastAPI对比(独立使用)
When to Choose FastAPI over Flask:
- Need async/await for high concurrency
- Want automatic API documentation (OpenAPI/Swagger)
- Require built-in data validation (Pydantic)
- Building modern microservices or GraphQL APIs
- Need WebSocket or Server-Sent Events support
When to Stick with Flask:
- Building traditional server-rendered web apps
- Existing large Flask codebase
- Need mature ecosystem and extensive plugins
- Team familiar with synchronous Python patterns
- Simpler deployment (no async runtime complexity)
Migration Considerations:
python
undefined何时选择FastAPI而非Flask:
- 需要async/await实现高并发
- 想要自动生成API文档(OpenAPI/Swagger)
- 需要内置数据验证(Pydantic)
- 构建现代微服务或GraphQL API
- 需要WebSocket或Server-Sent Events支持
何时坚持使用Flask:
- 构建传统服务器渲染的Web应用
- 已有大型Flask代码库
- 需要成熟的生态系统和丰富的插件
- 团队熟悉同步Python模式
- 部署更简单(无异步运行时复杂度)
迁移注意事项:
python
undefinedFlask pattern
Flask模式
@app.route('/users/int:user_id')
def get_user(user_id):
user = User.query.get_or_404(user_id)
return jsonify({'id': user.id, 'name': user.name})
@app.route('/users/int:user_id')
def get_user(user_id):
user = User.query.get_or_404(user_id)
return jsonify({'id': user.id, 'name': user.name})
FastAPI equivalent
FastAPI等效实现
@app.get('/users/{user_id}', response_model=UserResponse)
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
user = await db.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
[Full pytest, SQLAlchemy, and FastAPI patterns available in respective skills if deployed together]@app.get('/users/{user_id}', response_model=UserResponse)
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
user = await db.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="用户未找到")
return user
[完整的pytest、SQLAlchemy和FastAPI模式可在相关技能中获取(需一起部署)]