【问题标题】:API JSON Schema Validation with Optional Element using PydanticAPI JSON Schema Validation with Optional Element 使用 Pydantic
【发布时间】:2021-01-15 23:04:27
【问题描述】:

我正在使用 pydantic 的 fastapi 和 BaseModel 来验证和记录 API 返回的 JSON 模式。

这适用于固定返回,但我有更改返回的可选参数,因此我想将其包含在验证中,但当参数丢失且 API 中未返回该字段时它不会失败。

例如:我有一个名为 transparency 的可选布尔参数,当它设置为 true 时,我会返回一个名为 search_transparency 的块,并返回弹性查询。

{
  "info": {
    "totalrecords": 52
  },
  "records": [],
  "search_transparency": {"full_query": "blah blah"}
}

如果没有设置参数transparency=true 我希望返回为:

{
  "info": {
    "totalrecords": 52
  },
  "records": []
}

但是,当我在 pydantic 中将该元素设置为 Optional 时,我得到的是返回:

{
  "info": {
    "totalrecords": 52
  },
  "records": [],
  "search_transparency": None
}

我对记录下的字段有类似的东西。默认是最小的字段返回,但是如果您设置参数full=true,那么您会返回更多字段。我想以类似的方式处理这个问题,只是字段不存在而不是显示为None

这就是我使用 pydantic 处理它的方式:

class Info(BaseModel):
    totalrecords: int

class Transparency(BaseModel):
    full_query: str

class V1Place(BaseModel):
    name: str

class V1PlaceAPI(BaseModel):
    info: Info
    records: List[V1Place] = []
    search_transparency: Optional[Transparency]

这就是我使用 fastapi 执行验证的方式:

@app.get("/api/v1/place/search", response_model=V1PlaceAPI, tags=["v1_api"])

我怀疑也许我想要实现的是糟糕的 API 实践,也许我不应该有可变回报。

我是否应该创建多个单独的端点来处理这个问题?

例如。 api/v1/place/search?q=testapi/v1/place/full/transparent/search?q=test

编辑

我的 API 函数的更多细节:

@app.get("/api/v1/place/search", response_model=V1PlaceAPI, tags=["v1_api"])

def v1_place_search(q: str = Query(None, min_length=3, max_length=500, title="search through all place fields"),
                    transparency: Optional[bool] = None,
                    offset: Optional[int] = Query(0),
                    limit: Optional[int] = Query(15)):

    search_limit = offset + limit

    results, transparency_query = ESQuery(client=es_client,
                                          index='places',
                                          transparency=transparency,
                                          track_hits=True,
                                          offset=offset,
                                          limit=search_limit)

    return v1_place_parse(results.to_dict(), 
    show_transparency=transparency_query)

其中 ESQuery 只返回一个弹性搜索响应。 这是我的解析函数:

def v1_place_parse(resp, show_transparency=None):
    """This takes a response from elasticsearch and parses it for our legacy V1 elasticapi

    Args:
        resp (dict): This is the response from Search.execute after passing to_dict()

    Returns:
        dict: A dictionary that is passed to API
    """

    new_resp = {}
    total_records = resp['hits']['total']['value']
    query_records = len(resp.get('hits', {}).get('hits', []))

    new_resp['info'] = {'totalrecords': total_records,
                        'totalrecords_relation': resp['hits']['total']['relation'],
                        'totalrecordsperquery': query_records,
                        }
    if show_transparency is not None:
        search_string = show_transparency.get('query', '')
        new_resp['search_transparency'] = {'full_query': str(search_string),
                                           'components': {}}
    new_resp['records'] = []
    for hit in resp.get('hits', {}).get('hits', []):
        new_record = hit['_source']
        new_resp['records'].append(new_record)

    return new_resp

【问题讨论】:

  • 您可以返回两个类的联合,其中一个是透明的,而另一个不是。您介意展示您正在使用的端点功能吗?所以我可以提供一个接近您需求的示例
  • 谢谢 - 我已经添加了详细信息

标签: rest jsonschema fastapi pydantic


【解决方案1】:

如果是None,可能排除该字段即可完成工作。

只需添加一个response_model_exclude_none = True 作为路径参数

@app.get(
    "/api/v1/place/search",
    response_model=V1PlaceAPI,
    tags=["v1_api"],
    response_model_exclude_none=True,
)

您可以更多地自定义您的响应模型,这是我的一个很好的解释 answer 我真的建议您检查一下。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-20
    • 2020-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-05
    • 2021-06-27
    • 1970-01-01
    相关资源
    最近更新 更多