简体   繁体   English

使用 pydantic @validator 装饰器捕获错误以进行 fastapi 输入查询

[英]catch errors using pydantic @validator decorator for fastapi input query

I have 2 issues that I cannot tackle so far using docs/goole/stack info:-)到目前为止,我有 2 个问题无法使用 docs/goole/stack info 解决:-)

1) How shall I catch ValidationErrors thrown by pydantic model validator decorator when using model as query input for fastapi get query? 1)当使用 model 作为 fastapi get 查询的查询输入时,我应该如何捕获 pydantic model 验证器装饰器抛出的 ValidationErrors?

context:语境:

I have a pydantic model:我有一个 pydantic model:

class PointRankReqParams(BaseModel):
    rat: Optional[Literal['2G', '3G', '4G', '5G']] = '3G'
    h3resolution: Optional[int] = 5
    input_srid: Optional[int] = 4326
    bufferdistance: Optional[int] = 5000
    x: float 
    y: float 

    @validator('h3resolution')
    def h3resolution_bins(cls, v):
        if v not in [4,5,6,7,8]:
            raise ValueError('Hexabin size not permitted')
        return v

and FastAPI func that serves GET requests:和服务 GET 请求的 FastAPI 函数:

@router.get("/point/overall", summary="GET Ranking for given location point")
async def get_ranking_for_location_point(inputparams: PointRankReqParams = Depends(),token: str = Depends(oauth2_scheme)):    
    logger.debug("Here I am")
    return {}

Now when I curl with parameter h3resolution beyond defined range eg http://localhost:9000/rank/point/overall?rat=2G&h3resolution=9&input_srid=4326&bufferdistance=5000&x=21&y=21 I get errors in console that actually makes sense现在,当我的 curl 参数h3resolution超出定义范围时,例如http://localhost:9000/rank/point/overall?rat=2G&h3resolution=9&input_srid=4326&bufferdistance=5000&x=21&y=21在控制台中得到错误实际上是有道理的

py-    |   File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
py-    | pydantic.error_wrappers.ValidationError: 1 validation error for PointRankReqParams
py-    | h3resolution
py-    |   Hexabin size not permitted (type=value_error)

But FastAPI returns HTTP 500 Internal Server Error to end-client.但是 FastAPI 向终端客户端返回 HTTP 500 Internal Server Error。 How shall I catch those exceptions that comes from validator?我应该如何捕捉那些来自验证器的异常?


2. How to use Optional[Literal[...]] construction properly? 2. 如何正确使用 Optional[Literal[...]] 构造?

I've tried to redefine above example to use in model:我尝试重新定义上面的示例以在 model 中使用:

class PointRankReqParams(BaseModel):
...
h3resolution: Optional[Literal[4,5,6,7,8]] = 5

But when I curl GET query eg http://localhost:9000/rank/point/overall?rat=2G&h3resolution=7&input_srid=4326&bufferdistance=5000&x=21&y=21 I receive 422 Unprocessable entry as h3resolution is being interpreted as string not integer: But when I curl GET query eg http://localhost:9000/rank/point/overall?rat=2G&h3resolution=7&input_srid=4326&bufferdistance=5000&x=21&y=21 I receive 422 Unprocessable entry as h3resolution is being interpreted as string not integer:

{
  "detail": [
    {
      "loc": [
        "query",
        "h3resolution"
      ],
      "msg": "unexpected value; permitted: 4, 5, 6, 7, 8",
      "type": "value_error.const",
      "ctx": {
        "given": "7",
        "permitted": [
          4,
          5,
          6,
          7,
          8
        ]
      }
    }
  ]
}

Thanks a lot!非常感谢!

This is really good question, That's been discussed for several years, you can check this github issue .这是一个非常好的问题,已经讨论了好几年,你可以查看这个github 问题 And unfortunately, there's no consensus on how to handle it, documentation just shows a simple use case using plain Python classes.不幸的是,对于如何处理它没有达成共识, 文档只显示了一个使用普通 Python 类的简单用例。 It works with BaseModel and dataclass until you want to have custom validators or something, FastAPI's author says :它与BaseModeldataclass一起使用,直到您想要自定义验证器或其他东西,FastAPI 的作者

Having a single Pydantic model for query parameters could be interpreted as:具有单个 Pydantic model 用于查询参数可以解释为:

http://somedomain.com/?args={"k1":"v1","k2":"v2"}

or或者

http://somedomain.com/?k1=v1&k2=v2

or many other alternatives...或许多其他选择...

And we are not even discussing sub-models, that are valid in Pydantic models (and request bodies) but the behavior would be undefined for non-body parameters (query, path, etc).我们甚至没有讨论在 Pydantic 模型(和请求主体)中有效的子模型,但对于非主体参数(查询、路径等),行为将是未定义的。

There's no obvious way to go about how it would be interpreted that works for all the cases (including other people's use cases, future use cases, etc). go 没有明显的方法来解释如何解释它适用于所有情况(包括其他人的用例、未来用例等)。

So it doesn't really make sense to have it in FastAPI for a custom use case as it's very subjective and dependent on the conventions of the team.因此,将它放在 FastAPI 中用于自定义用例并没有什么意义,因为它非常主观并且依赖于团队的约定。

jimcarreer suggested using something like: jimcarreer建议使用类似的东西:

class PagingQuery(BaseModel):
    page: conint(ge=1)
    page_size: conint(ge=1, le=250) = 50

    @classmethod
    async def depends(cls, page: int = 1, page_size: int = 50):
        try:
            return cls(page=page, page_size=page_size)
        except ValidationError as e:
            errors = e.errors()
            for error in errors:
                error['loc'] = ['query'] + list(error['loc'])
            raise HTTPException(422, detail=errors)


@app.get("/example", tags=["basic"])
def example(paging: PagingQuery = Depends(PagingQuery.depends)):
    return {"page": paging.page, "page_size": paging.page_size}

But that looks hacky and I would rather stay away from it.但这看起来很老套,我宁愿远离它。

Answering your second question, the query string is just a string (like 'age=20&name=John' ) and key, value pairs are just strings;回答你的第二个问题,查询字符串只是一个字符串(如'age=20&name=John' ),键、值对只是字符串; you have specified Literal type, so there won't be type casting and you'll get an exception.你已经指定了Literal类型,所以不会有类型转换,你会得到一个异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM