Rest 使用Pydantic使用可选元素进行API JSON模式验证
我正在使用pydantic的fastapi和BaseModel来验证和记录API返回的JSON模式 这对于固定返回非常有效,但我有一些可选参数可以更改返回,因此我希望将其包括在验证中,但在参数丢失且API中未返回字段时,它不会失败 例如:我有一个可选的布尔参数,名为Rest 使用Pydantic使用可选元素进行API JSON模式验证,rest,jsonschema,fastapi,pydantic,Rest,Jsonschema,Fastapi,Pydantic,我正在使用pydantic的fastapi和BaseModel来验证和记录API返回的JSON模式 这对于固定返回非常有效,但我有一些可选参数可以更改返回,因此我希望将其包括在验证中,但在参数丢失且API中未返回字段时,它不会失败 例如:我有一个可选的布尔参数,名为transparency,当该参数设置为true时,我返回一个名为search\u transparency的块,并返回弹性查询 { "info": { "totalrecords"
transparency
,当该参数设置为true时,我返回一个名为search\u transparency的块,并返回弹性查询
{
"info": {
"totalrecords": 52
},
"records": [],
"search_transparency": {"full_query": "blah blah"}
}
如果未设置参数transparency=true
,我希望返回值为:
{
"info": {
"totalrecords": 52
},
"records": []
}
但是,当我在pydantic中将该元素设置为可选时,返回的结果是:
{
"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\u model=V1PlaceAPI,tags=[“v1\u api”)
我怀疑,也许我试图实现的是糟糕的API实践,也许我不应该有可变的回报
我应该创建多个单独的端点来处理这个问题吗
例如api/v1/place/search?q=test
vsapi/v1/place/full/transparent/search?q=test
编辑
有关my 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)
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
其中,ESQuery仅返回elasticsearch响应。
这是我的解析函数:
@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)
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
如果该字段为
None
,则可能排除该字段可以完成作业
只需添加一个response\u model\u exclude\u none=True
作为路径参数
@app.get(
"/api/v1/place/search",
response_model=V1PlaceAPI,
tags=["v1_api"],
response_model_exclude_none=True,
)
您可以进一步定制您的响应模型,下面是我的一个很好的解释,我建议您检查一下。您可以返回两个类的并集,其中一个是透明的,而另一个不是透明的。您介意显示您正在使用的端点函数吗?因此,我可以提供一个接近您需要的示例,谢谢-我已经添加了这个细节