6

I have a model like :

# Imports
from pydantic import BaseModel

# Data Models
class MyModel(BaseModel):
    a: str
    b: str
    c: str

@app.post('/endpoint_to_post')
async def post_log(my_model: MyModel):

I want to specify some constraint on this model. Indeed, I need a possible values constraint on the field C of the model MyModel.

Like:

# Imports
from pydantic import BaseModel

# Data Models
class MyModel(BaseModel):
    a: str
    b: str
    c: str in ['possible_value_1', 'possible_value_2']

Thank for your help :)

Chris
  • 18,724
  • 6
  • 46
  • 80
PicxyB
  • 596
  • 2
  • 8
  • 27
  • 1
    Does this answer your question? [How to require predefined string values in python pydantic basemodels?](https://stackoverflow.com/questions/61238502/how-to-require-predefined-string-values-in-python-pydantic-basemodels) – Drdilyor Jun 02 '21 at 18:43
  • I didn't try it, but seems a good solution. Thank! – PicxyB Jun 03 '21 at 07:45
  • Does this answer your question? [How to add drop down menu to Swagger UI autodocs based on BaseModel using FastAPI?](https://stackoverflow.com/questions/74366289/how-to-add-drop-down-menu-to-swagger-ui-autodocs-based-on-basemodel-using-fastap) – Chris Dec 22 '22 at 10:32

3 Answers3

15

Use Literal https://peps.python.org/pep-0586/

from pydantic import BaseModel
from typing import Literal

# Data Models
class MyModel(BaseModel):
    a: str
    b: str
    c: Literal['possible_value_1', 'possible_value_2']
Ben
  • 5,952
  • 4
  • 33
  • 44
Omer Ben Haim
  • 595
  • 4
  • 14
9

You can use enum from Python stdlib:

from enum import Enum
from pydantic import BaseModel


class CEnum(Enum):
    VALUE_1 = 'possible_value_1'
    VALUE_2 = 'possible_value_2'


# Data Models
class MyModel(BaseModel):
    a: str
    b: str
    c: CEnum

Pydantic will automatically convert any string matching the enum value to the correct enum instance and will raise ValidationError if it doesn't match anything. You can combine it with Optional or Union from Python's typing to either make this field optional or to allow other types as well (the first matching type from all types passed to Union will be used by Pydantic, so you can create a "catch-all" scenario using Union[CEnum, str]).

GwynBleidD
  • 20,081
  • 5
  • 46
  • 77
  • Thank for your answer :) I found another soltuion using pydantic.validator (and an unique model) if you want to check – PicxyB Jun 02 '21 at 13:44
  • This is much flexible. – MSS Jun 17 '22 at 17:52
  • This works but you will get an Enum after deserialization. If you *need it to be string* after deserialization, `Literal` is more appropriate as answered here https://stackoverflow.com/a/74113892/14052910. – Miroslav Valcicak Jan 03 '23 at 12:22
  • ConfigDict has a flag `use_enum_values = True` for this case https://docs.pydantic.dev/latest/api/config/#pydantic.config.ConfigDict.use_enum_values – Louis Maddox Aug 13 '23 at 13:45
4

I found a solution,

I used pydantic.validator to achieve this.

Example:

# Imports
from pydantic import BaseModel, validator

# Data Models
class MyModel(BaseModel):
    a: str
    b: str
    c: str # in ['possible_value_1', 'possible_value_2']

    @validator('c')
    def c_match(cls, v):
        if not v in ['possible_value_1', 'possible_value_2']:
            raise ValueError('c must be in [possible_value_1, possible_value_2]')
        return v

cf https://pydantic-docs.helpmanual.io/usage/validators/

PicxyB
  • 596
  • 2
  • 8
  • 27