Search code examples
pythondynamicfastapipydanticuvicorn

How can I use dynamically defined enums in FastAPI/Pydantic models run on Uvicorn?


I have a FastAPI application that has some Pydantic models defined. Some of the fields in these models should only allow values that are defined by enums that are loaded from a database and created at runtime.

The setup I have currently works fine when either debugging or running via the python command line however when I run with Uvicorn using uvicorn main:app I can see that the enums get loaded successfully but it seems the models get loaded before the enums and as such the app fails to start up successfully with an ImportError.

I have a minimal example that shows the issue on github.

I've tried changing it so instead of using an import statement I get the enum definition from the enum_definitions singleton declared on line 139 of models/enums.py, i.e.

Enum1 = enum_definitions.get_enum_definition("Enum1")

which triggered the exception on line 104 of models/enums.py. I've tried changing it so the enums are loaded both in main.py before the FastAPI object is instantiated and in models/model1.py before the model class is declared, however all these approaches result in the model not being able to see the enum.

How can I get the model to use these dynamic enums?

Many thanks in advance.


Solution

  • I was able to run this:

    1. Instead of importing directly Enum1, make Model class dynamically retrieve when needed, in file models/model1.py
        from pydantic import BaseModel, validator
        from models.enums import enum_definitions
        
        class Model(BaseModel):
            enum_field_name: str
        
            @validator('enum_field_name', pre=True, always=True)
            def validate_enum_field(cls, v):
                Enum1 = enum_definitions.get_enum_definition("Enum1")
                if v not in Enum1.__members__:
                    raise ValueError(f"{v} is not a valid value for 
    
    Enum1")
                return v
    
    1. Also, update your application startup In file main.py, Put after v1 = FastAPI(lifespan=lifespan)
        @app.on_event("startup")
        async def load_enums():
            await enum_definitions.load_definitions(global_import=True)