Easiest: Use typing.Literal
Literal is a good option when you can hardcode your values:
class Input(BaseModel):
option: Literal["foo", "bar"]
To use a dynamic list, wrap your list with a tuple:
allowed_values = ["foo", "bar"]
class Input(BaseModel):
option: Literal[tuple(allowed_values)]
Alt: Use Validator
Assuming it is not possible to transcode into regex (say you have objects, not only strings), you would then want to use a field validator:
allowed_values = ["foo", "bar"]
class Input(BaseModel):
option: str
@field_validator("option")
def validate_option(cls, v):
assert v in allowed_values
return v
Best: Reusable Field
Let's say this field is going to be reused in your codebase. A better approach would be to create a "custom field type" as such:
from typing import Annotated, Literal
allowed_values = ["foo", "bar"]
custom_option = Annotated[Literal[tuple(allowed_values)]]
class Input(BaseModel):
option: custom_option
Reusable field with Annotated Validator
Previously, I had this as the answer. Will keep for posterity.
You can add validators1 to reusable fields, which are neat:
from typing import Annotated
from pydantic import BaseModel, AfterValidator
allowed_values = ["foo", "bar"]
def option_in_allowed_values(v):
"""Ensures option is allowed"""
assert v in allowed_values
return v
custom_option = Annotated[str, AfterValidator(option_in_allowed_values)]
class Input(BaseModel):
option: custom_option