异步之舞:FastAPI与MongoDB的深度协奏
title: 异步之舞:FastAPI与MongoDB的深度协奏


pip install fastapi==0.95.0
pip install motor==3.1.2
pip install pydantic==1.10.7
pip install python-multipart==0.0.6
pip install uvicorn==0.21.1
from fastapi import FastAPI
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseModel, Field
import os
app = FastAPI()
# MongoDB配置模型
class MongoDBConfig:
MONGO_URI = os.getenv("MONGO_URI", "mongodb://localhost:27017")
DB_NAME = "fastapi_demo"
COLLECTION = "users"
# 异步数据库客户端
@app.on_event("startup")
async def startup_db_client():
app.mongodb_client = AsyncIOMotorClient(MongoDBConfig.MONGO_URI)
app.mongodb = app.mongodb_client[MongoDBConfig.DB_NAME]
@app.on_event("shutdown")
async def shutdown_db_client():
app.mongodb_client.close()
from bson import ObjectId
from typing import Optional
class PyObjectId(ObjectId):
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, v):
if not ObjectId.is_valid(v):
raise ValueError("Invalid ObjectId")
return ObjectId(v)
class UserCreate(BaseModel):
name: str = Field(..., min_length=3)
age: int = Field(..., gt=0)
tags: list[str] = []
class UserResponse(UserCreate):
id: PyObjectId = Field(default_factory=PyObjectId, alias="_id")
class Config:
json_encoders = {ObjectId: str}
@app.post("/users/")
async def create_user(user: User):
result = await db.users.insert_one(user.dict())
return {"id": str(result.inserted_id)}
@app.get("/users/{user_id}")
async def read_user(user_id: str):
if not ObjectId.is_valid(user_id):
raise HTTPException(400, "Invalid ID format")
user = await db.users.find_one({"_id": ObjectId(user_id)})
if not user:
raise HTTPException(404, "User not found")
# 转换 MongoDB 的 ObjectId 为字符串
user["id"] = str(user.pop("_id"))
return user
@app.patch("/users/{user_id}")
async def update_user(user_id: str, update_data: dict):
# 过滤无效字段
valid_fields = User.__annotations__.keys()
filtered_data = {k: v for k, v in update_data.items() if k in valid_fields}
result = await db.users.update_one(
{"_id": ObjectId(user_id)},
{"$set": filtered_data}
)
return {"modified_count": result.modified_count}
from fastapi import APIRouter
router = APIRouter()
@router.get("/users/stats/age-distribution")
async def get_age_distribution():
pipeline = [
{"$group": {
"_id": "$age",
"count": {"$sum": 1}
}},
{"$sort": {"_id": 1}}
]
results = []
async for doc in app.mongodb.users.aggregate(pipeline):
results.append({
"age": doc["_id"],
"count": doc["count"]
})
return results
async def create_indexes():
# 单字段索引
await app.mongodb.users.create_index("name", unique=True)
# 复合索引
await app.mongodb.users.create_index([("age", 1), ("tags", 1)])
# 文本索引
await app.mongodb.users.create_index([("name", "text")])
评论
发表评论