Pydantic:使用文字验证可区分的联合
Pydantic: Validate discriminated union with literals
我正在尝试使用 Literal
与 Pydantic 建立有区别的联盟。
有关于 Job 资源的事件,我想用 event_name
来区分它们。对于 JobPublishedEvents
,我想确保存在一些 extra_field
。
class GenericJobEvent(BaseModel):
event_name: str
id: int
class JobPublishedEvent(GenericJobEvent):
event_name: Literal['job.published']
extra_field: str
class Wrapper(BaseModel):
wrapped: Union[JobPublishedEvent, GenericJobEvent]
print(type(Wrapper(wrapped={'event_name': 'some.event', 'id': 1}).wrapped)) # GenericJobEvent
print(type(Wrapper(wrapped={'event_name': 'job.published', 'id': 1, 'extra_field': 'extra'}).wrapped)) # JobPublishedEvent
print(type(Wrapper(wrapped={'event_name': 'job.published', 'id': 1}).wrapped)) # GenericJobEvent
前两种情况的表现符合预期,对于第三种情况,我想要一个验证错误,因为文字匹配,但架构未实现。不过,我明白为什么回退到 GenericJobEvent 是有效的。
有人知道如何实现吗?
问题是由于 JobPublishedEvent
失败,而 GenericJobEvent
没有。想一想,event_name
属性是一个字符串,id
是一个int。一切都匹配。
所以,您可以做的是验证 GenericJobEvent
中的 event_name
并拒绝事件名称 job.published
您可以按照 docs
中的说明对其进行验证
@validator('event_name')
def event_name_filter(cls, v):
if 'job.published' == v:
raise ValueError('GenericJobEvent cannot have event_name equal to job.published')
return v.title()
我正在尝试使用 Literal
与 Pydantic 建立有区别的联盟。
有关于 Job 资源的事件,我想用 event_name
来区分它们。对于 JobPublishedEvents
,我想确保存在一些 extra_field
。
class GenericJobEvent(BaseModel):
event_name: str
id: int
class JobPublishedEvent(GenericJobEvent):
event_name: Literal['job.published']
extra_field: str
class Wrapper(BaseModel):
wrapped: Union[JobPublishedEvent, GenericJobEvent]
print(type(Wrapper(wrapped={'event_name': 'some.event', 'id': 1}).wrapped)) # GenericJobEvent
print(type(Wrapper(wrapped={'event_name': 'job.published', 'id': 1, 'extra_field': 'extra'}).wrapped)) # JobPublishedEvent
print(type(Wrapper(wrapped={'event_name': 'job.published', 'id': 1}).wrapped)) # GenericJobEvent
前两种情况的表现符合预期,对于第三种情况,我想要一个验证错误,因为文字匹配,但架构未实现。不过,我明白为什么回退到 GenericJobEvent 是有效的。
有人知道如何实现吗?
问题是由于 JobPublishedEvent
失败,而 GenericJobEvent
没有。想一想,event_name
属性是一个字符串,id
是一个int。一切都匹配。
所以,您可以做的是验证 GenericJobEvent
中的 event_name
并拒绝事件名称 job.published
您可以按照 docs
中的说明对其进行验证@validator('event_name')
def event_name_filter(cls, v):
if 'job.published' == v:
raise ValueError('GenericJobEvent cannot have event_name equal to job.published')
return v.title()