使用pydantic对模型的子类进行反序列化



我使用的数据遵循类继承模式…对于某些用例,我在让pydantic正确反序列化它时遇到了麻烦。

根据下面的代码,在使用parse_*方法时似乎没有调用验证器。"蓬松"的类型和";tiger"是Animal,然而,当反序列化"bob"Person,他的宠物是正确的Dog型。

有别的方法可以解决这个问题吗?使用pydantic不是必需的,但是反序列化嵌套复杂类型(包括对象的ListDict)的能力是必需的。

# modified from the following examples
# - https://github.com/samuelcolvin/pydantic/issues/2177#issuecomment-739578307
# - https://github.com/samuelcolvin/pydantic/issues/619#issuecomment-713508861
from pydantic import BaseModel
TIGER = """{ "type": "cat", "name": "Tiger the Cat", "color": "tabby" }"""
FLUFFY = """{ "type": "dog", "name": "Fluffy the Dog", "color": "brown", "breed": "rottweiler" }"""
ALICE = """{ "name": "Alice the Person" }"""
BOB = f"""{{ "name": "Bob the Person", "pet": {FLUFFY} }}"""
class Animal(BaseModel):
type: str
name: str
color: str = None
_subtypes_ = dict()
def __init_subclass__(cls, type=None):
cls._subtypes_[type or cls.__name__.lower()] = cls
@classmethod
def __get_validators__(cls):
yield cls._convert_to_real_type_
@classmethod
def _convert_to_real_type_(cls, data):
data_type = data.get("type")
if data_type is None:
raise ValueError("Missing 'type' in Animal")
sub = cls._subtypes_.get(data_type)
if sub is None:
raise TypeError(f"Unsupport sub-type: {data_type}")
return sub(**data)
class Cat(Animal, type="cat"):
hairless: bool = False
class Dog(Animal, type="dog"):
breed: str
class Person(BaseModel):
name: str
pet: Animal = None
tiger = Animal.parse_raw(TIGER)
print(f"tiger [{tiger.name}] => {type(tiger)} [{tiger.type}]")
fluffy = Animal.parse_raw(FLUFFY)
print(f"fluffy [{fluffy.name}] => {type(fluffy)} [{fluffy.type}]")
bob = Person.parse_raw(BOB)
pet = bob.pet
print(f"bob [{bob.name}] => {type(bob)}")
print(f"pet [{pet.name}] => {type(pet)}")

输出:

tiger [Tiger the Cat] => <class '__main__.Animal'>
fluffy [Fluffy the Dog] => <class '__main__.Animal'>
bob [Bob the Person] => <class '__main__.Person'>
pet [Fluffy the Dog] => <class '__main__.Dog'>

pydantic项目讨论的答案:

你可以添加

class Animal(BaseModel):
...

@classmethod
def parse_obj(cls, obj):
return cls._convert_to_real_type_(obj)