重复检查FastAPI中ID路径参数

发布于 2025-02-10 07:26:33 字数 509 浏览 2 评论 0原文

我有以下路线规格:

GET    /councils/{id}
PUT    /councils/{id}
DELETE /councils/{id}

在所有三个路线中,我必须在数据库中检查是否存在id的理事会,例如:

council = crud.council.get_by_id(db=db, id=id)
    if not council:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail='Council not found'
        )

这增加了代码中的许多样板。有什么方法可以减少这一点吗?我已经想到了创建依赖性,但是随后我必须为数据库中的不同模型编写不同的依赖性功能。有什么标准做法吗? 谢谢。

I have the following route specs:

GET    /councils/{id}
PUT    /councils/{id}
DELETE /councils/{id}

In all three routes, I have to check in the database whether the council with the id exists, like this:

council = crud.council.get_by_id(db=db, id=id)
    if not council:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail='Council not found'
        )

Which adds to a lot of boilerplate in the code. Is there any way of reducing this? I have thought of creating a dependency but then I have to write different dependency function for different models in my database. Is there any standard practice for this?
Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦途 2025-02-17 07:26:33

使用依赖性是要走的方法 - 它允许您提取围绕“从URL获得有效理事会”的逻辑;如果要映射/</>/< id>始终从特定模型中检索某些内容,则可以进一步概括它;但是 - 您可能需要根据一组可能的值对此进行验证,以避免人们试图在模型类中加载随机的Python标识符。

from fastapi import Path

def get_council_from_path(council_id: int = Path(...),
                          db: Session = Depends(get_db),
):
  council = crud.council.get_by_id(db=db, id=id)

  if not council:
    raise HTTPException(
      status_code=status.HTTP_404_NOT_FOUND,
      detail='Council not found'
    )

  return council


@app.get('/councils/{council_id}')
def get_council(council: Council = Depends(get_council_from_path)):
  pass

您可以概括依赖关系定义以使其可重复使用(我现在没有任何可用的测试,但是这个想法应该起作用):

def get_model_from_path(model, db: Session = Depends(get_db)):
  def get_model_from_id(id: int = Path(...)):
    obj = model.get_by_id(db=db, id=id)

    if not council:
      raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail='{type(model)} not found'
      )

  return get_model_from_id


@app.get('/councils/{id}')
def get_council(council: Council = Depends(get_model_from_path(crud.council)):
  pass

Using a dependency is the way to go - it allows you to extract the logic around "get a valid council from the URL"; you can generalize it further if you want to map /<model>/<id> to always retrieving something from a specific model; however - you might want to validate this against a set of possible values to avoid people trying to make you load random Python identifiers in your models class.

from fastapi import Path

def get_council_from_path(council_id: int = Path(...),
                          db: Session = Depends(get_db),
):
  council = crud.council.get_by_id(db=db, id=id)

  if not council:
    raise HTTPException(
      status_code=status.HTTP_404_NOT_FOUND,
      detail='Council not found'
    )

  return council


@app.get('/councils/{council_id}')
def get_council(council: Council = Depends(get_council_from_path)):
  pass

You can generalize the dependency definition to make it reusable (I don't have anything available to test this right now, but the idea should work):

def get_model_from_path(model, db: Session = Depends(get_db)):
  def get_model_from_id(id: int = Path(...)):
    obj = model.get_by_id(db=db, id=id)

    if not council:
      raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail='{type(model)} not found'
      )

  return get_model_from_id


@app.get('/councils/{id}')
def get_council(council: Council = Depends(get_model_from_path(crud.council)):
  pass
李不 2025-02-17 07:26:33

这是一种有效的解决方案。 FastApi支持类作为依赖项。因此,我可以拥有这样的类:

class DbObjIdValidator:
    def __init__(self, name: str):
        if name not in dir(crud):
            raise ValueError('Invalid model name specified')
        
        self.crud = getattr(crud, name)

    def __call__(self, db: Session = Depends(get_db), *, id: Any):
        db_obj =  self.crud.get_by_id(db=db, id=id)
        if not db_obj:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail='not found'
            )
        
        return db_obj

考虑crud模块将为所有ORM模型导入CRUD类。我的示例紧随 fastapapi cookiecutter项目他遵循的模式。

现在,为了使用它,例如在consems_route.py中,我可以做以下操作:

router = APIRouter(prefix='/councils')

council_id_dep = DbObjIdValidator('council') # name must be same as import name

@router.get('/{id}', response_model=schemas.CouncilDB)
def get_council_by_id(
    id: UUID, 
    db: Session = Depends(get_db),
    council: Councils = Depends(council_id_dep)    
):

    return council

Here is one solution that works. FastAPI supports classes as dependencies. Therefore I can have a class like this:

class DbObjIdValidator:
    def __init__(self, name: str):
        if name not in dir(crud):
            raise ValueError('Invalid model name specified')
        
        self.crud = getattr(crud, name)

    def __call__(self, db: Session = Depends(get_db), *, id: Any):
        db_obj =  self.crud.get_by_id(db=db, id=id)
        if not db_obj:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail='not found'
            )
        
        return db_obj

Considering crud module imports the crud classes for all the ORM models. My example is closely following the fastAPI cookiecutter project by Tiangolo, and the crud design pattern he followed.

Now in order to use it, for example in councils_route.py, I can do the following:

router = APIRouter(prefix='/councils')

council_id_dep = DbObjIdValidator('council') # name must be same as import name

@router.get('/{id}', response_model=schemas.CouncilDB)
def get_council_by_id(
    id: UUID, 
    db: Session = Depends(get_db),
    council: Councils = Depends(council_id_dep)    
):

    return council
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文