You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI中基于类的视图实现CRUD遇依赖注入错误求助

FastAPI 通用CRUD路由实现方案及错误排查

首先明确:FastAPI完全支持通用CRUD路由的实现,你的思路没问题,报错原因是对Depends的使用场景和通用路由的注册逻辑理解有误。

错误原因分析

  1. AttributeError: 'Depends' object has no attribute 'list'
    你大概率是把Depends直接绑定到了路由类的属性上(比如self.service = Depends(...)),然后直接调用self.service.list()。但Depends本身只是一个标记类,不是实际的服务实例,只有在路由函数内部被FastAPI解析后,才会生成对应的服务对象,直接访问Depends对象的方法自然会报错。

  2. AttributeError: 'Depends' object has no attribute 'routes'
    include_router要求传入的是APIRouter实例,而你用Depends包裹了这个参数,导致FastAPI去访问Depends对象的routes属性,这显然不符合预期。

正确实现方案

1. 先完善通用BaseService(基于你的已有实现)

from sqlalchemy.orm import Session
from typing import TypeVar, Generic, Type

ModelType = TypeVar("ModelType")

class BaseService(Generic[ModelType]):
    def __init__(self, model: Type[ModelType], db: Session):
        self.model = model
        self.db = db

    def list(self, skip: int = 0, limit: int = 100):
        return self.db.query(self.model).offset(skip).limit(limit).all()

    def get(self, item_id: int):
        return self.db.query(self.model).get(item_id)

    def create(self, obj_in):
        db_obj = self.model(**obj_in.dict())
        self.db.add(db_obj)
        self.db.commit()
        self.db.refresh(db_obj)
        return db_obj

    def update(self, db_obj, obj_in):
        for key, value in obj_in.dict(exclude_unset=True).items():
            setattr(db_obj, key, value)
        self.db.commit()
        self.db.refresh(db_obj)
        return db_obj

    def delete(self, db_obj):
        self.db.delete(db_obj)
        self.db.commit()
        return db_obj

2. 实现通用BaseRouter

核心是在路由函数内部通过Depends获取DB会话,再实例化对应服务,而不是在类属性层面绑定Depends:

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import TypeVar, Generic, Type, List
from pydantic import BaseModel

from your_db_module import get_db  # 后续会实现这个DB会话依赖

ModelType = TypeVar("ModelType")
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)
ServiceType = TypeVar("ServiceType", bound=BaseService)

class BaseRouter(Generic[ModelType, CreateSchemaType, UpdateSchemaType, ServiceType]):
    def __init__(self, 
                 model: Type[ModelType],
                 service_cls: Type[ServiceType],
                 create_schema: Type[CreateSchemaType],
                 update_schema: Type[UpdateSchemaType],
                 prefix: str):
        self.model = model
        self.service_cls = service_cls
        self.router = APIRouter(prefix=prefix, tags=[model.__name__])
        self.register_crud_routes(create_schema, update_schema)

    def register_crud_routes(self, create_schema, update_schema):
        # 列表接口
        @self.router.get("/", response_model=List[ModelType])
        def get_list(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
            service = self.service_cls(self.model, db)
            return service.list(skip=skip, limit=limit)

        # 详情接口
        @self.router.get("/{item_id}", response_model=ModelType)
        def get_detail(item_id: int, db: Session = Depends(get_db)):
            service = self.service_cls(self.model, db)
            item = service.get(item_id)
            if not item:
                raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Item not found")
            return item

        # 创建接口
        @self.router.post("/", response_model=ModelType, status_code=status.HTTP_201_CREATED)
        def create_item(item_in: create_schema, db: Session = Depends(get_db)):
            service = self.service_cls(self.model, db)
            return service.create(obj_in=item_in)

        # 更新接口
        @self.router.put("/{item_id}", response_model=ModelType)
        def update_item(item_id: int, item_in: update_schema, db: Session = Depends(get_db)):
            service = self.service_cls(self.model, db)
            db_item = service.get(item_id)
            if not db_item:
                raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Item not found")
            return service.update(db_obj=db_item, obj_in=item_in)

        # 删除接口
        @self.router.delete("/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
        def delete_item(item_id: int, db: Session = Depends(get_db)):
            service = self.service_cls(self.model, db)
            db_item = service.get(item_id)
            if not db_item:
                raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Item not found")
            service.delete(db_obj=db_item)

3. 数据库会话自动管理依赖

创建your_db_module.py,实现自动创建和关闭会话的依赖:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# 根据你的数据库类型修改URL
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"

engine = create_engine(
    SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}  # SQLite专属参数,其他数据库可移除
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

4. 实际使用通用路由

from fastapi import FastAPI
from your_models import User  # 你的SQLAlchemy模型
from your_schemas import UserCreate, UserUpdate  # 你的Pydantic请求/响应模型

# 创建用户模块的通用路由
user_router = BaseRouter(
    model=User,
    service_cls=BaseService,
    create_schema=UserCreate,
    update_schema=UserUpdate,
    prefix="/users"
).router

# 注册到主应用
app = FastAPI()
app.include_router(user_router)

扩展说明

  • 如果某个模型需要自定义CRUD逻辑,只需继承BaseService实现专属方法,在创建BaseRouter时传入该专属服务类即可。
  • 通用路由已经集成了DB会话的自动管理,每个请求都会获取独立会话,请求结束后自动关闭,无需手动处理。

内容的提问来源于stack exchange,提问作者Che el Horn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 16:15:42