如何在FastAPI主体验证中使用可区分的联合类型? (模型上的联盟)

问题描述 投票:0回答:3

我从 Typescript 中知道一个概念,称为“歧视工会”。这是你放置 2 个结构体(类等)的地方,并且类型是根据结构体的值决定的。我正在尝试通过 Pydantic 验证在 FastAPI 中实现类似的事情。我可以收到两种不同的请求负载。是其中之一取决于 accountType 变量。如果是

creative
,则应通过
RegistrationPayloadCreative
进行验证,如果是
brand
,则应通过
RegistrationPayloadBrand
进行验证。我该如何实现这一目标?找不到任何其他解决方案。
问题是它要么返回

unexpected value; permitted: 'creative' (type=value_error.const; given=brand; permitted=('creative',))

或者根本不起作用。

class RegistrationPayloadBase(BaseModel): first_name: str last_name: str email: str password: str class RegistrationPayloadCreative(RegistrationPayloadBase): accountType: Literal['creative'] class RegistrationPayloadBrand(RegistrationPayloadBase): company: str phone: str vat: str accountType: Literal['brand'] class A(BaseModel): b: Union[RegistrationPayloadBrand, RegistrationPayloadCreative] def main(): A(b={'first_name': 'sdf', 'last_name': 'sdf', 'email': 'sdf', 'password': 'sdfds', 'accountType': 'brand'}) if __name__ == '__main__': main()


python validation fastapi python-typing pydantic
3个回答
10
投票
__root__

parse_obj
来代替。
from typing import Union

from pydantic import BaseModel


class PlanetItem(BaseModel):
    id: str
    planet_name: str 
    # ...

class CarItem(BaseModel):
    id: str
    name: str
    # ...

class EitherItem(BaseModel):
    __root__: Union[PlanetItem, CarItem]



@app.get("/items/{item_id}", response_model=EitherItem)
def get_items(item_id):
    return EitherItem.parse_obj(response) # Now you get either PlanetItem or CarItem

信用:
https://github.com/tiangolo/fastapi/issues/2279#issuecomment-787517707


1
投票

所以:

>>> A(b={'first_name': 'sdf', 'last_name': 'sdf', 'email': 'sdf', 'password': 'sdfds', 'accountType': 'brand', 'company':'foo','vat':'bar', 'phone':'baz'}) A(b=RegistrationPayloadBrand(first_name='sdf', last_name='sdf', email='sdf', password='sdfds', company='foo', phone='baz', vat='bar', accountType='brand')) >>> A(b={'first_name': 'sdf', 'last_name': 'sdf', 'email': 'sdf', 'password': 'sdfds', 'accountType': 'creative'}) A(b=RegistrationPayloadCreative(first_name='sdf', last_name='sdf', email='sdf', password='sdfds', accountType='creative'))

或者将它们设置为可选(如果有效负载不一定包含这些字段)

class RegistrationPayloadBrand(RegistrationPayloadBase): company: Optional[str] phone: Optional[str] vat: Optional[str] accountType: Literal['brand'] >>> A(b={'first_name': 'sdf', 'last_name': 'sdf', 'email': 'sdf', 'password': 'sdfds', 'accountType': 'brand'}) A(b=RegistrationPayloadBrand(first_name='sdf', last_name='sdf', email='sdf', password='sdfds', company=None, phone=None, vat=None, accountType='brand')) >>> A(b={'first_name': 'sdf', 'last_name': 'sdf', 'email': 'sdf', 'password': 'sdfds', 'accountType': 'creative'}) A(b=RegistrationPayloadCreative(first_name='sdf', last_name='sdf', email='sdf', password='sdfds', accountType='creative'))

就能解决问题


0
投票
Field(discriminator='pipeline_name')

指定鉴别器来明确这一点。它为 FastAPI(或者我猜是 Pydantic)提供了一个明确的标准,用于决定如何确定有效负载是哪种联合类型。

class A(BaseModel):
    b: Union[RegistrationPayloadBrand, RegistrationPayloadCreative] = Field(discriminator='pipeline_name')

https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions

© www.soinside.com 2019 - 2024. All rights reserved.