英文:
How to add second Pydantic schema into Fastapi endpoint as an option
问题
如果参数等于'all',我想将'date'字段添加到模式中。
如果参数是'None',我想完全排除'data'字段。
英文:
Ok. Guys I can't figure it out. Is any option to add second Pydantic schema option, or switch between 2 Pydantic schemas according of parameter in request?
What I want to archieve. If paremeter equals 'all' I would like to add 'date' field into schema.
And if parameter is 'None' I would like to exclude 'data' field at all.
With code below with no parameter at all got: "date": null
[
{
"unit": "Xeikon 1",
"color": 65817763,
"bw": 552903,
"date": null
},
{
"unit": "Xeikon 2",
"color": 65825687,
"bw": 552903,
"date": null
},
{
"unit": "Xeikon 3",
"color": 65825784,
"bw": 552903,
"date": null
}
]
This is part of my code
class Clicks(BaseModel):
unit: str
color: int
bw: int
date: Optional[date]
class Config:
orm_mode = True
@xeikon_api.get('/xeikon/clicks/', response_model=List[schema.Clicks])
async def xeikon_Clicks_data(details: Optional[str]=None) :
"""
endpoint: list all Clicks data \n
"""
try:
if details == 'all':
return db.session.query(Clicks).all()
if details is None:
from main import session_local
response = session_local.query(Clicks)
return reorganizeClicksData(response)
except NoResultFound as n:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=n.orig.args,
) from n
答案1
得分: 1
根据你描述的方式,这将是两个不同的响应模式。因此,您需要为这两种响应定义两个不同的模型。
引用FastAPI文档的相关部分:
您可以声明响应为两种类型的
Union
,这意味着响应可以是其中的任何一种。在OpenAPI中,它将使用anyOf
定义。要实现此目的,请使用标准的Python类型提示typing.Union
。
为了简化你的例子,这是一个可能的解决方案:
from datetime import date
from typing import Optional, Union, List
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
class Clicks(BaseModel):
unit: str
color: int
bw: int
class Config:
orm_mode = True
class ExtendedClicks(Clicks):
date: date
AnyClicks = Union[List[ExtendedClicks], List[Clicks]]
app = FastAPI()
@app.get("/clicks")
async def endpoint(details: Optional[str] = None) -> AnyClicks:
# 获取点击数据:
test_data = [
{"unit": "foo", "color": 1, "bw": 1, "date": "2023-05-10"},
{"unit": "bar", "color": 0, "bw": 0, "date": "2023-01-01"},
]
if details == "all":
return [ExtendedClicks.parse_obj(obj) for obj in test_data]
if details is None:
return [Clicks.parse_obj(obj) for obj in test_data]
raise HTTPException(400)
类型联合中的顺序是重要的。由于ExtendedClicks
是Clicks
的子类型并且它只是忽略额外的值,如果我们在AnyClicks
中切换顺序,响应将始终匹配Clicks
,而您将永远看不到date
字段。
此示例中使用details=all
查询的响应:
[
{
"unit": "foo",
"color": 1,
"bw": 1,
"date": "2023-05-10"
},
{
"unit": "bar",
"color": 0,
"bw": 0,
"date": "2023-01-01"
}
]
没有查询参数的情况下的响应:
[
{
"unit": "foo",
"color": 1,
"bw": 1
},
{
"unit": "bar",
"color": 0,
"bw": 0
}
]
我仍然建议_不要_这样做,即不要使用返回类型的联合。无法通过OpenAPI模式表达响应模式取决于特定的查询参数。因此,从API设计的角度来看,我建议只为两个不同的模式使用两个单独的端点。
英文:
The way you describe it, that would be two distinct response schemas. Therefore you would have to define two distinct models for the response.
Let me quote the relevant section of the FastAPI docs:
> You can declare a response to be the Union
of two types, that means, that the response would be any of the two. It will be defined in OpenAPI with anyOf
. To do that, use the standard Python type hint typing.Union
.
To simplify your example a bit, here is a possible solution:
from datetime import date
from typing import Optional, Union
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
class Clicks(BaseModel):
unit: str
color: int
bw: int
class Config:
orm_mode = True
class ExtendedClicks(Clicks):
date: date
AnyClicks = Union[list[ExtendedClicks], list[Clicks]]
app = FastAPI()
@app.get("/clicks")
async def endpoint(details: Optional[str] = None) -> AnyClicks:
# Get the clicks data:
test_data = [
{"unit": "foo", "color": 1, "bw": 1, "date": "2023-05-10"},
{"unit": "bar", "color": 0, "bw": 0, "date": "2023-01-01"},
]
if details == "all":
return [ExtendedClicks.parse_obj(obj) for obj in test_data]
if details is None:
return [Clicks.parse_obj(obj) for obj in test_data]
raise HTTPException(400)
The order in the type union is important. Since ExtendedClicks
is a subtype of Clicks
and it simply ignores extra values, if we were to switch the order in AnyClicks
, the response would always match Clicks
and you would never see the date
field.
The response in this example with the details=all
query:
[
{
"unit": "foo",
"color": 1,
"bw": 1,
"date": "2023-05-10"
},
{
"unit": "bar",
"color": 0,
"bw": 0,
"date": "2023-01-01"
}
]
With no query parameters:
[
{
"unit": "foo",
"color": 1,
"bw": 1
},
{
"unit": "bar",
"color": 0,
"bw": 0
}
]
I would still recommend not doing that, i.e. not using a union of return types. There is no way to express via the OpenAPI schema that the response schema depends on specific query parameters. So all that one can see from the endpoint schema is that it may return a list of Clicks
and it also may return a list of ExtendedClicks
.
From an API design standpoint I would recommend just having two separate endpoints for the two distinct schemas.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论