在序列化 ORM 模型时,我需要将Pydantic指向不同的属性。alias=似乎没有按预期工作。在下面的示例中,我有一个具有id和uuid属性的 ORM 对象。我想序列uuid化为id.
alias=
id
uuid
API 响应应该是:
{ "id": "12345678-1234-5678-1234-567812345678", "foo": "bar" }
完整示例:
from uuid import UUID from fastapi import FastAPI from pydantic import BaseModel, Field from dataclasses import dataclass class ApiSchema(BaseModel): class Config: orm_mode = True uuid: UUID = Field(alias='id') foo: str | None = None @dataclass class ORMModel: id: int uuid: UUID foo: str = 'bar' app = FastAPI() @app.get("/") def endpoint() -> ApiSchema: t = ORMModel(id=1, uuid=UUID('12345678123456781234567812345678'), foo='bar') return t
这提高了
File fastapi/routing.py", line 141, in serialize_response raise ValidationError(errors, field.type_) pydantic.error_wrappers.ValidationError: 1 validation error for ApiSchema response -> id value is not a valid uuid (type=type_error.uuid)
相当于我想要实现的是这样的:
import marshmallow as ma class ApiSchema(ma.Schema): id = ma.fields.UUID(attribute='uuid') foo = ma.fields.Str()
您误解了别名的工作原理。当填充字段时,字段上的别名优先(高于实际字段名称)。这意味着,在初始化期间,该类将在它应该解析的数据中查找字段的别名。
按照您定义的方式ApiSchema,该字段uuid具有别名id。因此,当您解析一个实例时ORMModel(通过 发生在幕后的 FastAPI 中ApiSchema.from_orm),ApiSchema该类将查找id在该ORMModel对象上命名的属性以填充该uuid字段。
ApiSchema
ORMModel
ApiSchema.from_orm
由于您ORMModel实际上有一个名为的属性id(在您的示例中具有值)1,因此将其值分配给uuid.ApiSchema
1
显然,整数1不是UUID对象,不能强制转换为一个对象,因此您会收到验证错误,告诉您它找到的值id不是有效的 UUID。
UUID
这是归结为要点的问题:
from uuid import UUID from pydantic import BaseModel, Field, ValidationError class ApiSchema(BaseModel): uuid: UUID = Field(alias='id') foo: str | None = None try: ApiSchema.parse_obj({"uuid": "this is ignored", "foo": "bar"}) except ValidationError as exc: print(exc.json(indent=2)) try: ApiSchema.parse_obj({"id": 1, "foo": "bar"}) except ValidationError as exc: print(exc.json(indent=2))
第一次尝试的输出:
[ { "loc": [ "id" ], "msg": "field required", "type": "value_error.missing" } ]
第二:
[ { "loc": [ "id" ], "msg": "value is not a valid uuid", "type": "type_error.uuid" } ]
我想你想要相反的方式。我假设您的实际目标是id在您的ApiSchema模型上命名一个字段(并出现在您的 API 端点中)并将其别名为uuid,以便它ORMModel.uuid在初始化期间采用属性的值:
ORMModel.uuid
from uuid import UUID from pydantic import BaseModel, Field class ApiSchema(BaseModel): id: UUID = Field(alias="uuid") foo: str | None = None obj = ApiSchema.parse_obj( { "id": "this is ignored", "uuid": UUID("12345678123456781234567812345678"), "foo": "bar", } ) print(obj.json(indent=2))
输出:
因此,要修复您的 FastAPI 示例,您可能会这样做:
from dataclasses import dataclass from uuid import UUID from fastapi import FastAPI from pydantic import BaseModel, Field class ApiSchema(BaseModel): id: UUID = Field(alias="uuid") foo: str | None = None class Config: orm_mode = True @dataclass class ORMModel: id: int uuid: UUID foo: str = "bar" app = FastAPI() @app.get("/", response_model=ApiSchema, response_model_by_alias=False) def endpoint() -> ORMModel: t = ORMModel(id=1, uuid=UUID("12345678123456781234567812345678"), foo="bar") return t
旁注:是的,的实际返回类型endpoint是ORMModel. 装饰器返回的包装器然后将其转换为ApiSchemavia的实例from_orm。
endpoint
from_orm
忘记了最后一部分以实际获得您想要的响应。您需要response_model_by_alias=False在路由装饰器(True默认情况下)中设置响应以实际使用常规字段名称而不是别名。我相应地修复了最后一个代码。现在响应将是:
response_model_by_alias=False
True
{"id":"12345678-1234-5678-1234-567812345678","foo":"bar"}
在 PydanticBaseModel.json方法中,by_alias参数具有False默认值。FastAPI 的做法不同。
BaseModel.json
by_alias
False