'Pydantic/SQLAlchemy: How to work with enums?

What is the best way to convert a sqlalchemy model to a pydantic schema (model) if it includes an enum field?

Sqlalchemy

import enum
from sqlalchemy import Enum, Column, String
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class StateEnum(enum.Enum):
    CREATED = 'CREATED'
    UPDATED = 'UPDATED'

class Adapter(Base):
    __tablename__ = 'adapters'
    id = Column(String, primary_key=True)
    friendly_name = Column(String(256), nullable=False)
    state: StateEnum = Column(Enum(StateEnum))

Pydantic

from pydantic import BaseModel
from enum import Enum

class StateEnumDTO(str, Enum):
    CREATED = 'CREATED'
    UPDATED = 'UPDATED'


class AdapterDTO(BaseModel):
    friendly_name: str
    state: StateEnumDTO  # This currently cannot be converted?

    class Config:
        allow_population_by_field_name = True
        orm_mode = True
        use_enum_values = True

Conversion

AdapterDTO.from_orm(Adapter(friendly_name='test', state=StateEnum.CREATED))

This leads to the error

value is not a valid enumeration member; permitted: 'CREATED', 'UPDATED' (type=type_error.enum; enum_values=[<StateEnumDTO.CREATED: 'CREATED'>, <StateEnumDTO.UPDATED: 'UPDATED'>])

How can I configure either

a.) the serialization with the from_orm method?

or

b.) the creation of the state field?

c.) How to convert it the other way around?

Is there a native way to do this with pydantic or how is this typically done?

Update: Test case

def test_enum_conversion_to_dto():
    adapter = Adapter(id='1', friendly_name='test', state=StateEnum.CREATED)
    adapter_dto = AdapterDTO.from_orm(adapter)
    assert adapter_dto.state == StateEnumDTO.CREATED
    assert adapter_dto.state.value == StateEnum.CREATED.value


Solution 1:[1]

Pydantic requires that both enum classes have the same type definition.

In your case, StateEnum inherits from enum.Enum, but StateEnumDTO inherits from both str and enum.Enum.

You can fix this issue by changing your SQLAlchemy enum definition:

class StateEnum(str, enum.Enum):
    CREATED = 'CREATED'
    UPDATED = 'UPDATED'

Solution 2:[2]

You must add arbitrary_types_allowed = True To the model Config class.

from pydantic import BaseModel
from enum import Enum

class StateEnumDTO(str, Enum):
    CREATED = 'CREATED'
    UPDATED = 'UPDATED'


class AdapterDTO(BaseModel):
    friendly_name: str
    state: StateEnumDTO  # This currently cannot be converted?

    class Config:
        allow_population_by_field_name = True
        orm_mode = True
        use_enum_values = True
        arbitrary_types_allowed = True

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 bmoix
Solution 2 baudneo