How to use Pydantic Sub-Class Field Typing?

39 Views Asked by At

I want to validate a field in a pydantic model that is a subclass of another model. The class assigned to this field can change, but will always be a subclass of this specific model.

I am getting an error that should not be occurring given the inheritance and sub-class definition of the models. I do not think this should be happening. I have reported the bug on the github repo.

I have created a reprex so you can see the error firsthand:

from fastapi.datastructures import QueryParams
from pydantic import ConfigDict, Field, BaseModel
from humbldata.core.standard_models.abstract.humblobject import HumblObject
from humbldata.core.standard_models.toolbox import ToolboxQueryParams
from humbldata.core.standard_models.toolbox.technical.mandelbrot_channel import MandelbrotChannelQueryParams


class Model(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)
    command_params: QueryParams = Field()

    # @field_validator("command_params")
    # def validate_command_params(cls, v):
    #     if issubclass(type(v), QueryParams):
    #         return v
    #     raise TypeError("Wrong type for 'some_foo', must be subclass of Foo")


Model(command_params=MandelbrotChannelQueryParams())

I get this error:

---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
Cell In[23], line 19
     10     command_params: QueryParams = Field()
     12     # @field_validator("command_params")
     13     # def validate_command_params(cls, v):
     14     #     if issubclass(type(v), QueryParams):
     15     #         return v
     16     #     raise TypeError("Wrong type for 'some_foo', must be subclass of Foo")
---> 19 Model(command_params=MandelbrotChannelQueryParams())

File c:\Users\jjfan\github\humblFINANCE-org\humbldata\menv\Lib\site-packages\pydantic\main.py:171, in BaseModel.__init__(self, **data)
    169 # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
    170 __tracebackhide__ = True
--> 171 self.__pydantic_validator__.validate_python(data, self_instance=self)

ValidationError: 1 validation error for Model
command_params
  Input should be an instance of QueryParams [type=is_instance_of, input_value=MandelbrotChannelQueryPar...False, live_price=False), input_type=MandelbrotChannelQueryParams]
    For further information visit https://errors.pydantic.dev/2.6/v/is_instance_of
1

There are 1 best solutions below

0
JJ Fantini On

Here is the logic for the answer:

from pydantic import BaseModel, Field
from pydantic.config import ConfigDict


class QueryParams(BaseModel):
    pass


class subQueryParams(QueryParams):
    pass


class YourModel(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)
    command_params: QueryParams = Field()


YourModel(command_params=subQueryParams())

and here is a custom validator that works if you name your subclass with a prefix to the super class:

     @field_validator("command_params")
     def validate_command_params(cls, v):
         class_name = v.__class__.__name__
         if "QueryParams" in class_name:
             return v
         msg = "Wrong type for 'command_params', must be subclass of QueryParams"
         raise TypeError(msg)