Why does a Pydantic.BaseModel derived class can not instantiate a contained class?

Viewed 425

Using Python3

I have the following code:

    class Suitcase():
    def __init__(self):
        self.weight = 32


class Cargo(BaseModel):
    name: Optional[str] = "Mine"
    suitcase: Optional[Suitcase] = None


    def check(self):
        cargo.suitcase = Suitcase()

at some point in time I run the following code:

cargo = Cargo()
print(cargo)

the error I get is

class Cargo(BaseModel):
  File "pydantic/main.py", line 204, in pydantic.main.ModelMetaclass.__new__
  File "pydantic/fields.py", line 488, in pydantic.fields.ModelField.infer
  File "pydantic/fields.py", line 419, in pydantic.fields.ModelField.__init__
  File "pydantic/fields.py", line 539, in pydantic.fields.ModelField.prepare
  File "pydantic/fields.py", line 801, in pydantic.fields.ModelField.populate_validators
  File "pydantic/validators.py", line 723, in find_validators
RuntimeError: no validator found for <class '__main__.Suitcase'>, see `arbitrary_types_allowed` in Config

Is it because a BaseModel derived class can not contain other classes?

What do you think?

1 Answers

A pydantic model can contain other classes. In the pydantic documentation you'll find this under Model Config, with further information on custom types here:

arbitrary_types_allowed

whether to allow arbitrary user types for fields (they are validated simply by checking if the value is an instance of the type). If False, RuntimeError will be raised on model declaration (default: False). See an example in Field Types.

Suitcase is a custom type, so you should configure Cargo to allow arbitrary types. You have to define a config for your class, and set arbitrary_types_allowed = True:

class Suitcase():

    def __init__(self):
        self.weight = 32


class Cargo(BaseModel):
    name: Optional[str] = "Mine"
    suitcase: Optional[Suitcase] = None
        
    class Config:
        arbitrary_types_allowed = True


    def check(self):
        cargo.suitcase = Suitcase()

You could also add a custom validator for the field Cargo.suitcase, example:

class Cargo(BaseModel):
    name: Optional[str] = "Mine"
    suitcase: Optional[Suitcase] = None
        
    class Config:
        arbitrary_types_allowed = True

    @validator('suitcase')
    def validate_suitcase(cls, v):
        assert v.weight <= 32

    def check(self):
        cargo.suitcase = Suitcase()

Disclimer: Code not tested

Related