Home > other >  Pydantic with enum subclasses?
Pydantic with enum subclasses?

Time:10-05

So imagine I have something like

class SpecialEnum(Enum):
    def do_stuff(self):
         # some cool operation

And then have

class MySpecialEnum(SpecialDescriptorEnum):
   A = 1
   B = 2 

Then, I have a pydantic model with:

class MyModel(BaseModel):
    my_field: SpecialEnum

I want pydantic to validate that my_field is some instance of a SpecialEnum subclass. However, pydantic uses its special enum validator instead, and validates with SpecialEnum(field_value) instead, resulting in what will always be a ValidationError - since SpecialEnum was only ever meant to provide functionality or interface, and not values.

Is there any workaround for that?

CodePudding user response:

No need for a workaround. You can override the default validation by adding the special __get_validators__ class method to your base SpecialEnum class. Here is a full working example:

from __future__ import annotations
from collections.abc import Callable, Iterator
from enum import Enum
from typing import Any

from pydantic import BaseModel


class SpecialEnum(Enum):
    @classmethod
    def __get_validators__(cls) -> Iterator[Callable[..., Any]]:
        yield cls.validate

    @classmethod
    def validate(cls, v: SpecialEnum) -> SpecialEnum:
        if not isinstance(v, cls):
            raise TypeError(f"Must be an instance of `{cls.__name__}`")
        return v

    def do_stuff(self) -> None:
        print("stuff", self)


class MySpecialEnum(SpecialEnum):
    A = 1
    B = 2


class MyModel(BaseModel):
    my_field: SpecialEnum


class Unrelated(Enum):
    foo = "foo"


if __name__ == '__main__':
    instance = MyModel(my_field=MySpecialEnum.A)
    instance.my_field.do_stuff()
    # wrong1 = MyModel(my_field=Unrelated.foo)  # causes a `ValidationError`
    # wrong2 = MyModel(my_field=1)  # ALSO causes a `ValidationError`

There is a caveat here though. Usually you would be able to define some additional logic to accept the enum member's value or even allow the member's name and have the validator return the corresponding enum member itself. (The wrong2 example in the last line.)

But since your validation methods must be defined on the base class SpecialEnum because that is the one you use to annotate my_field, there is no way to know which enum subclass to check the value/name against.

If that is of interest to you, you might be able to construct some additional logic for that, but I think this will only be possible if you ensure that all enum members of all subclasses of SpecialEnum are distinct.

Hope this helps.

  • Related