系统地排除所有路线的字段
我发现最好使用一个具体但非常简单的示例。假设您有以下模型:
from pydantic import BaseModel, Field
class Demo(BaseModel):
foo: str
bar: str = Field(return_in_api=False)
我们希望确保bar
永远不会在响应中返回,无论是在response_model
明确作为参数提供给路由装饰器and当它只是设置为路由处理函数的返回注释时。 (假设我们不想使用内置的exclude
无论出于何种原因,我们的字段的参数。)
我发现最可靠的方法是子类化fastapi.routing.APIRoute https://github.com/tiangolo/fastapi/blob/0.95.1/fastapi/routing.py#L322并钩入其__init__
方法。通过复制父类的一小部分代码,我们可以确保我们始终获得正确的响应模型。一旦我们有了这个,只需设置路线的问题response_model_exclude
调用父构造函数之前的参数。
这是我的建议:
from collections.abc import Callable
from typing import Any
from fastapi.responses import Response
from fastapi.dependencies.utils import get_typed_return_annotation, lenient_issubclass
from fastapi.routing import APIRoute, Default, DefaultPlaceholder
class CustomAPIRoute(APIRoute):
def __init__(
self,
path: str,
endpoint: Callable[..., Any],
*,
response_model: Any = Default(None),
**kwargs: Any,
) -> None:
# We need this part to ensure we get the response model,
# even if it is just set as an annotation on the handler function.
if isinstance(response_model, DefaultPlaceholder):
return_annotation = get_typed_return_annotation(endpoint)
if lenient_issubclass(return_annotation, Response):
response_model = None
else:
response_model = return_annotation
# Find the fields to exclude:
if response_model is not None:
kwargs["response_model_exclude"] = {
name
for name, field in response_model.__fields__.items()
if field.field_info.extra.get("return_in_api") is False
}
super().__init__(path, endpoint, response_model=response_model, **kwargs)
我们现在可以在路由器上设置自定义路由类(文档 https://fastapi.tiangolo.com/advanced/custom-request-and-route/)。这样它将用于all其路线:
from fastapi import FastAPI
# ... import CustomAPIRoute
# ... import Demo
api = FastAPI()
api.router.route_class = CustomAPIRoute
@api.get("/demo1")
async def demo1() -> Demo:
return Demo(foo="a", bar="b")
@api.get("/demo2", response_model=Demo)
async def demo2() -> dict[str, Any]:
return {"foo": "x", "bar": "y"}
尝试这个简单的 API 示例uvicorn
and GET
设置端点/demo1
and /demo2
产生响应{"foo":"a"}
and {"foo":"x"}
分别。
确保模式一致性
然而值得一提的是(除非我们采取额外的步骤)bar
场将仍然是架构的一部分。这意味着例如为这两个端点自动生成的 OpenAPI 文档将会呈现 bar
作为预期响应的顶级属性。
这不是您问题的一部分,因此我认为您已经意识到这一点并正在采取措施确保一致性。如果没有,对于阅读本文的其他人,您可以定义一个静态schema_extra https://docs.pydantic.dev/usage/schema/#schema-customization方法上的Config
您的基本模型的删除那些在返回模式之前永远不会“向外部”显示的字段:
from typing import Any
from pydantic import BaseModel, Field
class CustomBaseModel(BaseModel):
class Config:
@staticmethod
def schema_extra(schema: dict[str, Any]) -> None:
properties = schema.get("properties", {})
to_delete = set()
for name, prop in properties.items():
if prop.get("return_in_api") is False:
to_delete.add(name)
for name in to_delete:
del properties[name]
class Demo(CustomBaseModel):
foo: str
bar: str = Field(return_in_api=False)