Python常用Web框架
框架对比
| 框架 | 类型 | 特点 | 适用场景 |
|---|---|---|---|
| Django | 全栈框架 | 功能丰富、内置ORM、Admin后台、认证系统 | 大型Web应用、企业级项目 |
| Flask | 微框架 | 轻量灵活、扩展丰富、学习曲线平缓 | 小型Web应用、API服务、原型开发 |
| FastAPI | 现代Web框架 | 高性能、自动文档、类型提示、异步支持 | RESTful API、微服务、高性能应用 |
Django
Django简介
Django是一个高级Python Web框架,鼓励快速开发和清洁、实用的设计。它由经验丰富的开发者构建,解决了Web开发中的许多常见问题,因此您可以专注于编写应用程序,而无需重新发明轮子。
安装Django
bash
pip install djangopip install django创建Django项目
bash
# 创建项目
django-admin startproject myproject
# 进入项目目录
cd myproject
# 创建应用
python manage.py startapp myapp
# 运行开发服务器
python manage.py runserver# 创建项目
django-admin startproject myproject
# 进入项目目录
cd myproject
# 创建应用
python manage.py startapp myapp
# 运行开发服务器
python manage.py runserverDjango基本结构
myproject/
├── manage.py # 项目管理脚本
├── myproject/ # 项目配置目录
│ ├── __init__.py
│ ├── asgi.py # ASGI入口
│ ├── settings.py # 项目设置
│ ├── urls.py # 主URL配置
│ └── wsgi.py # WSGI入口
└── myapp/ # 应用目录
├── __init__.py
├── admin.py # 后台管理配置
├── apps.py # 应用配置
├── migrations/ # 数据库迁移文件
├── models.py # 数据模型
├── tests.py # 测试文件
└── views.py # 视图函数myproject/
├── manage.py # 项目管理脚本
├── myproject/ # 项目配置目录
│ ├── __init__.py
│ ├── asgi.py # ASGI入口
│ ├── settings.py # 项目设置
│ ├── urls.py # 主URL配置
│ └── wsgi.py # WSGI入口
└── myapp/ # 应用目录
├── __init__.py
├── admin.py # 后台管理配置
├── apps.py # 应用配置
├── migrations/ # 数据库迁移文件
├── models.py # 数据模型
├── tests.py # 测试文件
└── views.py # 视图函数Django视图
python
# myapp/views.py
from django.http import HttpResponse
from django.shortcuts import render
# 函数视图
def index(request):
return HttpResponse("Hello, Django!")
# 类视图
from django.views import View
class MyView(View):
def get(self, request):
return HttpResponse("This is a GET request")
def post(self, request):
return HttpResponse("This is a POST request")# myapp/views.py
from django.http import HttpResponse
from django.shortcuts import render
# 函数视图
def index(request):
return HttpResponse("Hello, Django!")
# 类视图
from django.views import View
class MyView(View):
def get(self, request):
return HttpResponse("This is a GET request")
def post(self, request):
return HttpResponse("This is a POST request")Django路由
python
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('myapp/', include('myapp.urls')), # 包含应用的URL配置
]
# myapp/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'), # 函数视图路由
path('myview/', views.MyView.as_view(), name='myview'), # 类视图路由
]# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('myapp/', include('myapp.urls')), # 包含应用的URL配置
]
# myapp/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'), # 函数视图路由
path('myview/', views.MyView.as_view(), name='myview'), # 类视图路由
]Django模型
python
# myapp/models.py
from django.db import models
class User(models.Model):
name = models.CharField(max_length=100) # 字符串字段
email = models.EmailField(unique=True) # 邮箱字段,唯一约束
age = models.IntegerField(null=True, blank=True) # 整数字段,允许为空
created_at = models.DateTimeField(auto_now_add=True) # 创建时间,自动添加
updated_at = models.DateTimeField(auto_now=True) # 更新时间,自动更新
def __str__(self):
return self.name # 模型实例的字符串表示# myapp/models.py
from django.db import models
class User(models.Model):
name = models.CharField(max_length=100) # 字符串字段
email = models.EmailField(unique=True) # 邮箱字段,唯一约束
age = models.IntegerField(null=True, blank=True) # 整数字段,允许为空
created_at = models.DateTimeField(auto_now_add=True) # 创建时间,自动添加
updated_at = models.DateTimeField(auto_now=True) # 更新时间,自动更新
def __str__(self):
return self.name # 模型实例的字符串表示Django迁移
bash
# 创建迁移文件
python manage.py makemigrations
# 应用迁移
python manage.py migrate
# 查看迁移状态
python manage.py showmigrations# 创建迁移文件
python manage.py makemigrations
# 应用迁移
python manage.py migrate
# 查看迁移状态
python manage.py showmigrationsDjango Admin
Django内置了强大的后台管理系统,可以轻松管理数据库中的数据。
python
# myapp/admin.py
from django.contrib import admin
from .models import User
admin.site.register(User) # 注册模型到Admin
# 自定义Admin
class UserAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'age', 'created_at') # 列表显示字段
search_fields = ('name', 'email') # 搜索字段
list_filter = ('age', 'created_at') # 过滤字段
ordering = ('-created_at',) # 排序字段
admin.site.register(User, UserAdmin) # 注册自定义Admin# myapp/admin.py
from django.contrib import admin
from .models import User
admin.site.register(User) # 注册模型到Admin
# 自定义Admin
class UserAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'age', 'created_at') # 列表显示字段
search_fields = ('name', 'email') # 搜索字段
list_filter = ('age', 'created_at') # 过滤字段
ordering = ('-created_at',) # 排序字段
admin.site.register(User, UserAdmin) # 注册自定义AdminDjango表单
python
# myapp/forms.py
from django import forms
from .models import User
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('name', 'email', 'age') # 表单包含的字段
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'age': forms.NumberInput(attrs={'class': 'form-control'}),
}
# myapp/views.py
def create_user(request):
if request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid():
form.save() # 保存到数据库
return HttpResponse("User created successfully!")
else:
form = UserForm()
return render(request, 'create_user.html', {'form': form})# myapp/forms.py
from django import forms
from .models import User
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('name', 'email', 'age') # 表单包含的字段
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'age': forms.NumberInput(attrs={'class': 'form-control'}),
}
# myapp/views.py
def create_user(request):
if request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid():
form.save() # 保存到数据库
return HttpResponse("User created successfully!")
else:
form = UserForm()
return render(request, 'create_user.html', {'form': form})Django模板
html
<!-- myapp/templates/create_user.html -->
<!DOCTYPE html>
<html>
<head>
<title>Create User</title>
</head>
<body>
<h1>Create User</h1>
<form method="post">
{% csrf_token %} <!-- CSRF保护 -->
{{ form.as_p }} <!-- 渲染表单 -->
<button type="submit">Create</button>
</form>
</body>
</html><!-- myapp/templates/create_user.html -->
<!DOCTYPE html>
<html>
<head>
<title>Create User</title>
</head>
<body>
<h1>Create User</h1>
<form method="post">
{% csrf_token %} <!-- CSRF保护 -->
{{ form.as_p }} <!-- 渲染表单 -->
<button type="submit">Create</button>
</form>
</body>
</html>Django REST Framework (DRF)
Django REST Framework是Django的一个强大且灵活的工具包,用于构建Web API。
bash
pip install djangorestframeworkpip install djangorestframeworkpython
# myproject/settings.py
INSTALLED_APPS = [
# ...
'rest_framework',
]
# myapp/serializers.py
from rest_framework import serializers
from .models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__' # 序列化所有字段
# myapp/views.py
from rest_framework import viewsets
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
# myapp/urls.py
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = [
# ...
path('api/', include(router.urls)),
]# myproject/settings.py
INSTALLED_APPS = [
# ...
'rest_framework',
]
# myapp/serializers.py
from rest_framework import serializers
from .models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__' # 序列化所有字段
# myapp/views.py
from rest_framework import viewsets
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
# myapp/urls.py
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = [
# ...
path('api/', include(router.urls)),
]Flask
Flask简介
Flask是一个轻量级的WSGI Web应用框架。它旨在使入门快速且容易,并能够扩展到复杂的应用程序。Flask提供了建议,但不强制执行任何依赖项或项目布局。
安装Flask
bash
pip install flaskpip install flaskFlask基本示例
python
# app.py
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello, Flask!"
@app.route('/api', methods=['GET', 'POST'])
def api():
if request.method == 'GET':
return jsonify({"message": "This is a GET request"})
elif request.method == 'POST':
data = request.json
return jsonify({"message": "This is a POST request", "data": data})
if __name__ == '__main__':
app.run(debug=True)# app.py
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello, Flask!"
@app.route('/api', methods=['GET', 'POST'])
def api():
if request.method == 'GET':
return jsonify({"message": "This is a GET request"})
elif request.method == 'POST':
data = request.json
return jsonify({"message": "This is a POST request", "data": data})
if __name__ == '__main__':
app.run(debug=True)运行Flask应用
bash
python app.pypython app.pyFlask路由
python
# 基本路由
@app.route('/')
def index():
return 'Index Page'
# 带参数的路由
@app.route('/user/<username>')
def show_user_profile(username):
return f'User {username}'
# 带类型转换的路由
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'Post {post_id}'
# 多URL路由
@app.route('/')
@app.route('/index')
def index():
return 'Index Page'
# 不同HTTP方法的路由
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# 处理登录逻辑
return 'Login POST'
else:
# 显示登录表单
return 'Login GET'# 基本路由
@app.route('/')
def index():
return 'Index Page'
# 带参数的路由
@app.route('/user/<username>')
def show_user_profile(username):
return f'User {username}'
# 带类型转换的路由
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'Post {post_id}'
# 多URL路由
@app.route('/')
@app.route('/index')
def index():
return 'Index Page'
# 不同HTTP方法的路由
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# 处理登录逻辑
return 'Login POST'
else:
# 显示登录表单
return 'Login GET'Flask模板
bash
pip install jinja2pip install jinja2python
# app.py
from flask import render_template
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)# app.py
from flask import render_template
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)html
<!-- templates/hello.html -->
<!doctype html>
<html>
<head>
<title>Hello from Flask</title>
</head>
<body>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
</body>
</html><!-- templates/hello.html -->
<!doctype html>
<html>
<head>
<title>Hello from Flask</title>
</head>
<body>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
</body>
</html>Flask请求处理
python
from flask import request
@app.route('/login', methods=['POST'])
def login():
username = request.form['username'] # 表单数据
password = request.form['password']
json_data = request.json # JSON数据
args = request.args # URL参数
files = request.files['file'] # 文件上传
return f'Username: {username}'from flask import request
@app.route('/login', methods=['POST'])
def login():
username = request.form['username'] # 表单数据
password = request.form['password']
json_data = request.json # JSON数据
args = request.args # URL参数
files = request.files['file'] # 文件上传
return f'Username: {username}'Flask响应处理
python
from flask import jsonify, make_response
@app.route('/json')
def json_response():
return jsonify({"message": "Hello, JSON!"})
@app.route('/custom')
def custom_response():
response = make_response('Custom Response')
response.headers['Content-Type'] = 'text/plain'
response.status_code = 201
return responsefrom flask import jsonify, make_response
@app.route('/json')
def json_response():
return jsonify({"message": "Hello, JSON!"})
@app.route('/custom')
def custom_response():
response = make_response('Custom Response')
response.headers['Content-Type'] = 'text/plain'
response.status_code = 201
return responseFlask扩展
Flask有丰富的扩展生态系统,可以轻松添加各种功能:
| 扩展 | 功能 | 安装命令 |
|---|---|---|
| Flask-SQLAlchemy | ORM数据库集成 | pip install flask-sqlalchemy |
| Flask-Migrate | 数据库迁移 | pip install flask-migrate |
| Flask-RESTful | REST API开发 | pip install flask-restful |
| Flask-JWT-Extended | JWT认证 | pip install flask-jwt-extended |
| Flask-WTF | 表单处理 | pip install flask-wtf |
| Flask-Login | 用户认证 | pip install flask-login |
Flask-SQLAlchemy示例
python
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
# 定义模型
class User(db.Model):
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)
def __repr__(self):
return f'<User {self.username}>'
# 创建数据库
with app.app_context():
db.create_all()
# 使用模型
@app.route('/add_user/<username>/<email>')
def add_user(username, email):
user = User(username=username, email=email)
db.session.add(user)
db.session.commit()
return f'User {username} added'
@app.route('/users')
def get_users():
users = User.query.all()
return ', '.join([user.username for user in users])from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
# 定义模型
class User(db.Model):
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)
def __repr__(self):
return f'<User {self.username}>'
# 创建数据库
with app.app_context():
db.create_all()
# 使用模型
@app.route('/add_user/<username>/<email>')
def add_user(username, email):
user = User(username=username, email=email)
db.session.add(user)
db.session.commit()
return f'User {username} added'
@app.route('/users')
def get_users():
users = User.query.all()
return ', '.join([user.username for user in users])FastAPI
FastAPI简介
FastAPI是一个现代、快速(高性能)的Web框架,用于基于标准Python类型提示构建API。它具有以下特点:
- 快速:非常高的性能,与NodeJS和Go相当
- 快速编码:将开发速度提高约200%至300%
- 更少的错误:减少约40%的人为(开发者)错误
- 直观:强大的编辑器支持,自动补全无处不在
- 简单:易于学习和使用
- 短:减少代码重复
- 健壮:生产就绪代码,具有自动交互式文档
安装FastAPI
bash
pip install fastapi uvicornpip install fastapi uvicornFastAPI基本示例
python
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}运行FastAPI应用
bash
uvicorn main:app --reloaduvicorn main:app --reloadFastAPI类型提示
FastAPI充分利用Python的类型提示来自动验证请求数据和生成文档。
python
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# 定义数据模型
class Item(BaseModel):
name: str
price: float
is_offer: bool = None
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# 定义数据模型
class Item(BaseModel):
name: str
price: float
is_offer: bool = None
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}FastAPI路径参数
python
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}
# 路径参数验证
from fastapi import Path
@app.get("/items/{item_id}")
def read_item(item_id: int = Path(..., title="The ID of the item to get", ge=1)):
return {"item_id": item_id}@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}
# 路径参数验证
from fastapi import Path
@app.get("/items/{item_id}")
def read_item(item_id: int = Path(..., title="The ID of the item to get", ge=1)):
return {"item_id": item_id}FastAPI查询参数
python
@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
# 查询参数验证
from fastapi import Query
@app.get("/items/")
def read_items(q: str = Query(None, min_length=3, max_length=50, regex="^fixedquery$")):
return {"q": q}@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
# 查询参数验证
from fastapi import Query
@app.get("/items/")
def read_items(q: str = Query(None, min_length=3, max_length=50, regex="^fixedquery$")):
return {"q": q}FastAPI请求体
python
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
@app.post("/items/")
def create_item(item: Item):
return itemfrom pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
@app.post("/items/")
def create_item(item: Item):
return itemFastAPI响应模型
python
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
class ItemResponse(BaseModel):
name: str
price: float
tax: float = None
@app.post("/items/", response_model=ItemResponse)
def create_item(item: Item):
return itemfrom pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
class ItemResponse(BaseModel):
name: str
price: float
tax: float = None
@app.post("/items/", response_model=ItemResponse)
def create_item(item: Item):
return itemFastAPI异步支持
python
from fastapi import FastAPI
import asyncio
app = FastAPI()
@app.get("/")
async def read_root():
await asyncio.sleep(1) # 模拟异步操作
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
await asyncio.sleep(0.5) # 模拟异步操作
return {"item_id": item_id, "q": q}from fastapi import FastAPI
import asyncio
app = FastAPI()
@app.get("/")
async def read_root():
await asyncio.sleep(1) # 模拟异步操作
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
await asyncio.sleep(0.5) # 模拟异步操作
return {"item_id": item_id, "q": q}FastAPI依赖注入
python
from fastapi import FastAPI, Depends
app = FastAPI()
# 定义依赖
def get_db():
db = "Database Connection"
try:
yield db
finally:
# 关闭数据库连接
pass
# 使用依赖
@app.get("/items/")
def read_items(db: str = Depends(get_db)):
return {"db": db}from fastapi import FastAPI, Depends
app = FastAPI()
# 定义依赖
def get_db():
db = "Database Connection"
try:
yield db
finally:
# 关闭数据库连接
pass
# 使用依赖
@app.get("/items/")
def read_items(db: str = Depends(get_db)):
return {"db": db}FastAPI安全性
bash
pip install python-multipart python-jose[cryptography] passlib[bcrypt]pip install python-multipart python-jose[cryptography] passlib[bcrypt]python
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta
app = FastAPI()
# 配置
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# 密码加密
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# OAuth2密码Bearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# 模拟用户数据库
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
"disabled": False,
}
}
# 验证密码
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
# 获取用户
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return user_dict
# 认证用户
def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username)
if not user:
return False
if not verify_password(password, user["hashed_password"]):
return False
return user
# 创建访问令牌
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# 获取当前用户
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = get_user(fake_users_db, username=username)
if user is None:
raise credentials_exception
return user
# 登录端点
@app.post("/token")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["username"]}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
# 受保护的端点
@app.get("/users/me")
async def read_users_me(current_user: dict = Depends(get_current_user)):
return current_userfrom fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta
app = FastAPI()
# 配置
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# 密码加密
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# OAuth2密码Bearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# 模拟用户数据库
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
"disabled": False,
}
}
# 验证密码
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
# 获取用户
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return user_dict
# 认证用户
def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username)
if not user:
return False
if not verify_password(password, user["hashed_password"]):
return False
return user
# 创建访问令牌
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# 获取当前用户
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = get_user(fake_users_db, username=username)
if user is None:
raise credentials_exception
return user
# 登录端点
@app.post("/token")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["username"]}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
# 受保护的端点
@app.get("/users/me")
async def read_users_me(current_user: dict = Depends(get_current_user)):
return current_userPydantic v2(FastAPI 数据验证核心)
FastAPI 基于 Pydantic v2(Rust 实现,验证速度提升 5-50 倍):
python
from pydantic import BaseModel, Field, EmailStr, ValidationError
from datetime import datetime
# 定义模型
class UserCreate(BaseModel):
name: str = Field(min_length=1, max_length=50)
age: int = Field(ge=0, le=150)
email: EmailStr | None = None
tags: list[str] = []
created_at: datetime | None = None
model_config = { # Pydantic v2 配置方式
"extra": "forbid", # 禁止额外字段
"frozen": True, # 不可变对象
}
# 使用
try:
user = UserCreate(name="Alice", age=30, email="alice@example.com")
print(user.model_dump()) # v2 替代 .dict()
print(user.model_dump_json()) # v2 替代 .json()
except ValidationError as e:
print(e.errors())
# 自定义验证
from pydantic import field_validator, model_validator
class Order(BaseModel):
items: list[str]
total: float
@field_validator("total")
@classmethod
def check_total(cls, v: float) -> float:
if v < 0:
raise ValueError("金额不能为负")
return v
@model_validator(mode="after")
def check_items(self) -> "Order":
if not self.items and self.total > 0:
raise ValueError("有金额但无商品")
return self
# Pydantic v1 vs v2 对照
# .dict() → .model_dump()
# .json() → .model_dump_json()
# .schema() → .model_json_schema()
# class Config → model_config
# @validator → @field_validator
# @root_validator → @model_validatorfrom pydantic import BaseModel, Field, EmailStr, ValidationError
from datetime import datetime
# 定义模型
class UserCreate(BaseModel):
name: str = Field(min_length=1, max_length=50)
age: int = Field(ge=0, le=150)
email: EmailStr | None = None
tags: list[str] = []
created_at: datetime | None = None
model_config = { # Pydantic v2 配置方式
"extra": "forbid", # 禁止额外字段
"frozen": True, # 不可变对象
}
# 使用
try:
user = UserCreate(name="Alice", age=30, email="alice@example.com")
print(user.model_dump()) # v2 替代 .dict()
print(user.model_dump_json()) # v2 替代 .json()
except ValidationError as e:
print(e.errors())
# 自定义验证
from pydantic import field_validator, model_validator
class Order(BaseModel):
items: list[str]
total: float
@field_validator("total")
@classmethod
def check_total(cls, v: float) -> float:
if v < 0:
raise ValueError("金额不能为负")
return v
@model_validator(mode="after")
def check_items(self) -> "Order":
if not self.items and self.total > 0:
raise ValueError("有金额但无商品")
return self
# Pydantic v1 vs v2 对照
# .dict() → .model_dump()
# .json() → .model_dump_json()
# .schema() → .model_json_schema()
# class Config → model_config
# @validator → @field_validator
# @root_validator → @model_validatorFastAPI 进阶用法
python
from fastapi import FastAPI, Query, Path, Body, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Annotated
app = FastAPI(title="进阶 API", version="2.0.0")
# CORS 配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# 路径参数校验
@app.get("/items/{item_id}")
def read_item(
item_id: Annotated[int, Path(ge=1, le=1000)],
q: Annotated[str | None, Query(max_length=50)] = None,
):
return {"item_id": item_id, "q": q}
# 请求体
class Item(BaseModel):
name: str
price: Annotated[float, Field(gt=0)]
@app.post("/items", status_code=status.HTTP_201_CREATED)
def create_item(item: Item):
return item
# 响应模型(自动过滤敏感字段)
class UserOut(BaseModel):
id: int
name: str
@app.post("/users", response_model=UserOut)
def create_user(user: UserCreate):
# 即使返回了 password,也会被自动过滤
return {"id": 1, "name": user.name, "password": "secret"}
# 生命周期事件
@app.on_event("startup")
async def startup():
print("服务启动")
@app.on_event("shutdown")
async def shutdown():
print("服务关闭")from fastapi import FastAPI, Query, Path, Body, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Annotated
app = FastAPI(title="进阶 API", version="2.0.0")
# CORS 配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# 路径参数校验
@app.get("/items/{item_id}")
def read_item(
item_id: Annotated[int, Path(ge=1, le=1000)],
q: Annotated[str | None, Query(max_length=50)] = None,
):
return {"item_id": item_id, "q": q}
# 请求体
class Item(BaseModel):
name: str
price: Annotated[float, Field(gt=0)]
@app.post("/items", status_code=status.HTTP_201_CREATED)
def create_item(item: Item):
return item
# 响应模型(自动过滤敏感字段)
class UserOut(BaseModel):
id: int
name: str
@app.post("/users", response_model=UserOut)
def create_user(user: UserCreate):
# 即使返回了 password,也会被自动过滤
return {"id": 1, "name": user.name, "password": "secret"}
# 生命周期事件
@app.on_event("startup")
async def startup():
print("服务启动")
@app.on_event("shutdown")
async def shutdown():
print("服务关闭")FastAPI自动文档
FastAPI自动生成交互式API文档,您可以通过以下URL访问:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
FastAPI 项目实战(重点)
推荐项目结构
myapi/
├── app/
│ ├── __init__.py
│ ├── main.py # 应用入口,注册路由/中间件
│ ├── config.py # 配置管理(pydantic-settings)
│ ├── database.py # 数据库引擎和会话
│ ├── dependencies.py # 全局依赖(get_db, get_current_user)
│ ├── models/ # SQLAlchemy 模型
│ │ ├── __init__.py
│ │ └── user.py
│ ├── schemas/ # Pydantic 请求/响应模型
│ │ ├── __init__.py
│ │ └── user.py
│ ├── routers/ # 路由模块
│ │ ├── __init__.py
│ │ └── users.py
│ ├── services/ # 业务逻辑层
│ │ ├── __init__.py
│ │ └── user_service.py
│ └── utils/ # 工具函数
│ └── security.py
├── tests/
│ ├── __init__.py
│ └── test_users.py
├── alembic/ # 数据库迁移
├── .env
├── Dockerfile
├── docker-compose.yml
├── pyproject.toml
└── requirements.txtmyapi/
├── app/
│ ├── __init__.py
│ ├── main.py # 应用入口,注册路由/中间件
│ ├── config.py # 配置管理(pydantic-settings)
│ ├── database.py # 数据库引擎和会话
│ ├── dependencies.py # 全局依赖(get_db, get_current_user)
│ ├── models/ # SQLAlchemy 模型
│ │ ├── __init__.py
│ │ └── user.py
│ ├── schemas/ # Pydantic 请求/响应模型
│ │ ├── __init__.py
│ │ └── user.py
│ ├── routers/ # 路由模块
│ │ ├── __init__.py
│ │ └── users.py
│ ├── services/ # 业务逻辑层
│ │ ├── __init__.py
│ │ └── user_service.py
│ └── utils/ # 工具函数
│ └── security.py
├── tests/
│ ├── __init__.py
│ └── test_users.py
├── alembic/ # 数据库迁移
├── .env
├── Dockerfile
├── docker-compose.yml
├── pyproject.toml
└── requirements.txt配置管理
python
# app/config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "MyAPI"
debug: bool = False
database_url: str = "sqlite+aiosqlite:///./test.db"
redis_url: str = "redis://localhost:6379"
secret_key: str = "change-me"
access_token_expire_minutes: int = 30
model_config = {"env_file": ".env"}
settings = Settings()# app/config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "MyAPI"
debug: bool = False
database_url: str = "sqlite+aiosqlite:///./test.db"
redis_url: str = "redis://localhost:6379"
secret_key: str = "change-me"
access_token_expire_minutes: int = 30
model_config = {"env_file": ".env"}
settings = Settings()数据库会话管理
python
# app/database.py
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from sqlalchemy.orm import DeclarativeBase
from app.config import settings
engine = create_async_engine(settings.database_url, echo=settings.debug)
async_session = async_sessionmaker(engine, expire_on_commit=False)
class Base(DeclarativeBase):
pass
# 依赖:获取数据库会话
async def get_db() -> AsyncSession:
async with async_session() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise# app/database.py
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from sqlalchemy.orm import DeclarativeBase
from app.config import settings
engine = create_async_engine(settings.database_url, echo=settings.debug)
async_session = async_sessionmaker(engine, expire_on_commit=False)
class Base(DeclarativeBase):
pass
# 依赖:获取数据库会话
async def get_db() -> AsyncSession:
async with async_session() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise模型定义
python
# app/models/user.py
from sqlalchemy import String, Integer
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(50))
email: Mapped[str] = mapped_column(String(120), unique=True)
age: Mapped[int | None]# app/models/user.py
from sqlalchemy import String, Integer
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(50))
email: Mapped[str] = mapped_column(String(120), unique=True)
age: Mapped[int | None]Pydantic Schema
python
# app/schemas/user.py
from pydantic import BaseModel, Field, EmailStr
from datetime import datetime
# 创建用户请求
class UserCreate(BaseModel):
name: str = Field(min_length=1, max_length=50)
email: EmailStr
age: int | None = Field(None, ge=0, le=150)
# 用户响应(不返回敏感字段)
class UserOut(BaseModel):
id: int
name: str
email: str
created_at: datetime | None
model_config = {"from_attributes": True} # 支持 ORM 模式# app/schemas/user.py
from pydantic import BaseModel, Field, EmailStr
from datetime import datetime
# 创建用户请求
class UserCreate(BaseModel):
name: str = Field(min_length=1, max_length=50)
email: EmailStr
age: int | None = Field(None, ge=0, le=150)
# 用户响应(不返回敏感字段)
class UserOut(BaseModel):
id: int
name: str
email: str
created_at: datetime | None
model_config = {"from_attributes": True} # 支持 ORM 模式路由实现
python
# app/routers/users.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.models.user import User
from app.schemas.user import UserCreate, UserOut
router = APIRouter(prefix="/users", tags=["用户管理"])
# 创建用户
@router.post("/", response_model=UserOut, status_code=status.HTTP_201_CREATED)
async def create_user(data: UserCreate, db: AsyncSession = Depends(get_db)):
user = User(name=data.name, email=data.email, age=data.age)
db.add(user)
await db.flush()
await db.refresh(user)
return user
# 获取用户列表(分页)
@router.get("/", response_model=list[UserOut])
async def list_users(
skip: int = 0,
limit: int = 10,
db: AsyncSession = Depends(get_db),
):
stmt = select(User).offset(skip).limit(limit)
result = await db.execute(stmt)
return result.scalars().all()
# 获取单个用户
@router.get("/{user_id}", response_model=UserOut)
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
return user
# 删除用户
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_user(user_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
await db.delete(user)# app/routers/users.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.models.user import User
from app.schemas.user import UserCreate, UserOut
router = APIRouter(prefix="/users", tags=["用户管理"])
# 创建用户
@router.post("/", response_model=UserOut, status_code=status.HTTP_201_CREATED)
async def create_user(data: UserCreate, db: AsyncSession = Depends(get_db)):
user = User(name=data.name, email=data.email, age=data.age)
db.add(user)
await db.flush()
await db.refresh(user)
return user
# 获取用户列表(分页)
@router.get("/", response_model=list[UserOut])
async def list_users(
skip: int = 0,
limit: int = 10,
db: AsyncSession = Depends(get_db),
):
stmt = select(User).offset(skip).limit(limit)
result = await db.execute(stmt)
return result.scalars().all()
# 获取单个用户
@router.get("/{user_id}", response_model=UserOut)
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
return user
# 删除用户
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_user(user_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
await db.delete(user)服务层(业务逻辑)
python
# app/services/user_service.py
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.user import User
from app.schemas.user import UserCreate
class UserService:
def __init__(self, db: AsyncSession):
self.db = db
async def create(self, data: UserCreate) -> User:
# 检查邮箱是否已存在
result = await self.db.execute(
select(User).where(User.email == data.email)
)
if result.scalar_one_or_none():
raise ValueError("邮箱已被注册")
user = User(name=data.name, email=data.email, age=data.age)
self.db.add(user)
await self.db.flush()
await self.db.refresh(user)
return user
async def list(self, skip: int = 0, limit: int = 10) -> list[User]:
result = await self.db.execute(
select(User).offset(skip).limit(limit)
)
return result.scalars().all()# app/services/user_service.py
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.user import User
from app.schemas.user import UserCreate
class UserService:
def __init__(self, db: AsyncSession):
self.db = db
async def create(self, data: UserCreate) -> User:
# 检查邮箱是否已存在
result = await self.db.execute(
select(User).where(User.email == data.email)
)
if result.scalar_one_or_none():
raise ValueError("邮箱已被注册")
user = User(name=data.name, email=data.email, age=data.age)
self.db.add(user)
await self.db.flush()
await self.db.refresh(user)
return user
async def list(self, skip: int = 0, limit: int = 10) -> list[User]:
result = await self.db.execute(
select(User).offset(skip).limit(limit)
)
return result.scalars().all()全局异常处理
python
# app/main.py
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
# 全局异常处理器
@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
return JSONResponse(
status_code=400,
content={"detail": str(exc)},
)
# 统一响应格式
class APIResponse:
@staticmethod
def success(data=None, message="成功"):
return {"code": 0, "message": message, "data": data}
@staticmethod
def error(message="失败", code=-1):
return {"code": code, "message": message}# app/main.py
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
# 全局异常处理器
@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
return JSONResponse(
status_code=400,
content={"detail": str(exc)},
)
# 统一响应格式
class APIResponse:
@staticmethod
def success(data=None, message="成功"):
return {"code": 0, "message": message, "data": data}
@staticmethod
def error(message="失败", code=-1):
return {"code": code, "message": message}中间件(日志/耗时/限流)
python
import time
import logging
from fastapi import Request
logger = logging.getLogger("uvicorn")
@app.middleware("http")
async def log_requests(request: Request, call_next):
start = time.perf_counter()
response = await call_next(request)
elapsed = time.perf_counter() - start
logger.info(
f"{request.method} {request.url.path} "
f"{response.status_code} {elapsed:.3f}s"
)
response.headers["X-Process-Time"] = str(elapsed)
return responseimport time
import logging
from fastapi import Request
logger = logging.getLogger("uvicorn")
@app.middleware("http")
async def log_requests(request: Request, call_next):
start = time.perf_counter()
response = await call_next(request)
elapsed = time.perf_counter() - start
logger.info(
f"{request.method} {request.url.path} "
f"{response.status_code} {elapsed:.3f}s"
)
response.headers["X-Process-Time"] = str(elapsed)
return response文件上传
python
from fastapi import UploadFile, File
import aiofiles
@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
# 保存文件
content = await file.read()
async with aiofiles.open(f"uploads/{file.filename}", "wb") as f:
await f.write(content)
return {
"filename": file.filename,
"size": len(content),
"content_type": file.content_type,
}
# 多个文件上传
@app.post("/upload/multiple")
async def upload_multiple(files: list[UploadFile] = File(...)):
return [{"filename": f.filename} for f in files]from fastapi import UploadFile, File
import aiofiles
@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
# 保存文件
content = await file.read()
async with aiofiles.open(f"uploads/{file.filename}", "wb") as f:
await f.write(content)
return {
"filename": file.filename,
"size": len(content),
"content_type": file.content_type,
}
# 多个文件上传
@app.post("/upload/multiple")
async def upload_multiple(files: list[UploadFile] = File(...)):
return [{"filename": f.filename} for f in files]后台任务
python
from fastapi import BackgroundTasks
def send_email(email: str, body: str):
# 发送邮件的耗时操作
pass
@app.post("/send-notification")
async def send_notification(email: str, tasks: BackgroundTasks):
tasks.add_task(send_email, email, "欢迎注册!")
return {"message": "邮件将在后台发送"}from fastapi import BackgroundTasks
def send_email(email: str, body: str):
# 发送邮件的耗时操作
pass
@app.post("/send-notification")
async def send_notification(email: str, tasks: BackgroundTasks):
tasks.add_task(send_email, email, "欢迎注册!")
return {"message": "邮件将在后台发送"}分页工具
python
from math import ceil
from pydantic import BaseModel
class PaginationParams(BaseModel):
page: int = Field(default=1, ge=1)
page_size: int = Field(default=10, ge=1, le=100)
class PaginatedResult(BaseModel):
items: list
total: int
page: int
page_size: int
total_pages: int
def paginate(query, total: int, params: PaginationParams) -> PaginatedResult:
return PaginatedResult(
items=list(query),
total=total,
page=params.page,
page_size=params.page_size,
total_pages=ceil(total / params.page_size),
)
# 使用
@app.get("/users")
async def list_users(
pagination: Annotated[PaginationParams, Query()],
db: AsyncSession = Depends(get_db),
):
stmt = select(User).offset(
(pagination.page - 1) * pagination.page_size
).limit(pagination.page_size)
result = await db.execute(stmt)
users = result.scalars().all()
total_stmt = select(func.count(User.id))
total = (await db.execute(total_stmt)).scalar()
return paginate(users, total, pagination)from math import ceil
from pydantic import BaseModel
class PaginationParams(BaseModel):
page: int = Field(default=1, ge=1)
page_size: int = Field(default=10, ge=1, le=100)
class PaginatedResult(BaseModel):
items: list
total: int
page: int
page_size: int
total_pages: int
def paginate(query, total: int, params: PaginationParams) -> PaginatedResult:
return PaginatedResult(
items=list(query),
total=total,
page=params.page,
page_size=params.page_size,
total_pages=ceil(total / params.page_size),
)
# 使用
@app.get("/users")
async def list_users(
pagination: Annotated[PaginationParams, Query()],
db: AsyncSession = Depends(get_db),
):
stmt = select(User).offset(
(pagination.page - 1) * pagination.page_size
).limit(pagination.page_size)
result = await db.execute(stmt)
users = result.scalars().all()
total_stmt = select(func.count(User.id))
total = (await db.execute(total_stmt)).scalar()
return paginate(users, total, pagination)应用入口
python
# app/main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.config import settings
from app.database import engine, Base
from app.routers import users
# 生命周期管理
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时:创建表
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield
# 关闭时:释放资源
await engine.dispose()
app = FastAPI(
title=settings.app_name,
version="1.0.0",
lifespan=lifespan,
)
# 中间件
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
app.include_router(users.router, prefix="/api/v1")# app/main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.config import settings
from app.database import engine, Base
from app.routers import users
# 生命周期管理
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时:创建表
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield
# 关闭时:释放资源
await engine.dispose()
app = FastAPI(
title=settings.app_name,
version="1.0.0",
lifespan=lifespan,
)
# 中间件
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
app.include_router(users.router, prefix="/api/v1")测试
python
# tests/test_users.py
import pytest
from httpx import AsyncClient, ASGITransport
from app.main import app
from app.database import get_db, Base, engine, async_session
# 使用测试数据库
@pytest.fixture
async def db_session():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async with async_session() as session:
yield session
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
@pytest.fixture
async def client(db_session):
async def override_get_db():
yield db_session
app.dependency_overrides[get_db] = override_get_db
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
yield ac
@pytest.mark.asyncio
async def test_create_user(client):
response = await client.post("/api/v1/users/", json={
"name": "Alice",
"email": "alice@example.com",
"age": 30,
})
assert response.status_code == 201
data = response.json()
assert data["name"] == "Alice"
@pytest.mark.asyncio
async def test_list_users(client):
response = await client.get("/api/v1/users/")
assert response.status_code == 200# tests/test_users.py
import pytest
from httpx import AsyncClient, ASGITransport
from app.main import app
from app.database import get_db, Base, engine, async_session
# 使用测试数据库
@pytest.fixture
async def db_session():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async with async_session() as session:
yield session
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
@pytest.fixture
async def client(db_session):
async def override_get_db():
yield db_session
app.dependency_overrides[get_db] = override_get_db
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
yield ac
@pytest.mark.asyncio
async def test_create_user(client):
response = await client.post("/api/v1/users/", json={
"name": "Alice",
"email": "alice@example.com",
"age": 30,
})
assert response.status_code == 201
data = response.json()
assert data["name"] == "Alice"
@pytest.mark.asyncio
async def test_list_users(client):
response = await client.get("/api/v1/users/")
assert response.status_code == 200Docker 部署
dockerfile
# Dockerfile
FROM python:3.12-slim
WORKDIR /app
# 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制代码
COPY . .
# 启动
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]# Dockerfile
FROM python:3.12-slim
WORKDIR /app
# 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制代码
COPY . .
# 启动
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]yaml
# docker-compose.yml
version: "3.9"
services:
api:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql+asyncpg://user:pass@db:5432/myapp
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:# docker-compose.yml
version: "3.9"
services:
api:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql+asyncpg://user:pass@db:5432/myapp
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:常用依赖清单
txt
# requirements.txt / pyproject.toml dependencies
fastapi>=0.109 # Web 框架
uvicorn[standard] # ASGI 服务器
sqlalchemy>=2.0 # ORM
asyncpg # PostgreSQL 异步驱动
alembic # 数据库迁移
pydantic>=2.0 # 数据验证
pydantic-settings # 环境变量管理
python-jose # JWT 编解码
passlib[bcrypt] # 密码加密
python-multipart # 表单/文件上传
aiofiles # 异步文件操作
httpx # HTTP 客户端(测试/调用外部API)
celery[redis] # 任务队列(可选)
redis # 缓存(可选)# requirements.txt / pyproject.toml dependencies
fastapi>=0.109 # Web 框架
uvicorn[standard] # ASGI 服务器
sqlalchemy>=2.0 # ORM
asyncpg # PostgreSQL 异步驱动
alembic # 数据库迁移
pydantic>=2.0 # 数据验证
pydantic-settings # 环境变量管理
python-jose # JWT 编解码
passlib[bcrypt] # 密码加密
python-multipart # 表单/文件上传
aiofiles # 异步文件操作
httpx # HTTP 客户端(测试/调用外部API)
celery[redis] # 任务队列(可选)
redis # 缓存(可选)框架选择建议
Django:
- 适合需要快速开发大型Web应用的团队
- 内置功能丰富,减少第三方依赖
- 适合需要Admin后台的项目
Flask:
- 适合小型项目或API服务
- 适合学习Web开发的初学者
- 适合需要高度定制化的项目
FastAPI:
- 适合构建高性能的RESTful API
- 适合需要异步支持的项目
- 适合微服务架构
- 适合需要自动文档的项目
部署建议
Django:
- WSGI服务器:Gunicorn + Nginx
- 容器化:Docker + Kubernetes
- 云平台:AWS Elastic Beanstalk, Google App Engine
Flask:
- WSGI服务器:Gunicorn + Nginx
- 容器化:Docker + Kubernetes
- 云平台:Heroku, AWS Lambda
FastAPI:
- ASGI服务器:Uvicorn + Gunicorn + Nginx
- 容器化:Docker + Kubernetes
- 云平台:AWS Lambda, Google Cloud Functions
总结
Python提供了多种优秀的Web框架,选择合适的框架取决于项目的需求、团队的经验和项目的规模。无论选择哪种框架,都应该遵循最佳实践,编写清晰、可维护的代码,并确保应用的安全性和性能。
- Django适合需要全功能框架的大型项目
- Flask适合需要灵活性的小型项目和API服务
- FastAPI适合需要高性能和自动文档的现代API开发
通过学习和掌握这些框架,您可以构建各种类型的Web应用,从简单的API到复杂的企业级应用。
Cao Note