Search code examples
pythonpandasfastapipydantic

How to create schema with fixed values using pydantic


I have a json that looks like the following, exported from pandas Dataframe:

{"columns": ["x", "y", "z"], "data": [[0, 0, 0.5], [1, 1, null]]}

This, I want to send to a FastAPI and validate using pydantic.

  1. How can I enforce that "columns" equals the list ["x", "y", "z"]
  2. "data" is a list of format [int, int, Optional[float]]. How can I enforce that?

The closest I got so far is the following:

class MyModel(BaseModel):
    columns: List[Literal['x', 'y', 'z']] = ['x', 'y', 'z']
    data: List[conlist(Union[int, Optional[float]], min_length=3, max_length=3)]

However, this would also allow columns = ['x', 'x', 'x'] or data = [0,0,0]


Solution

  • You are on the right track. You can use field_validator to perform complex validations:

    class MyModel(BaseModel):
        columns: List[conlist(str, min_length=3, max_length=3)]
        data: List[conlist(Optional[float], min_items=3, max_items=3)]
        
        @field_validator("columns")
        def validate_columns(cls, value):
            for row in value:
                if row != ["x", "y", "z"]:
                    raise ValueError("columns must be exactly ['x', 'y', 'z']")
            return value
    
        @field_validator("data", mode="before")
        def validate_data(cls, value):
            for row in value:
                if not (isinstance(row[0], int) and isinstance(row[1], int)):
                    raise ValueError("First two values aren't int.")
                if row[2] is not None and not isinstance(row[2], float):
                    raise ValueError("The third value should be None or float.")
            return value