Can FastAPI/Pydantic individually validate input items in a list?

4.7k views Asked by At

I have a FastAPI post method:

from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
from typing import List

import pandas as pd

class InputItem(BaseModel):
    Feature: str

class Item(BaseModel):
    Feature: str
    Result: str

app = FastAPI()

@app.post("/", response_model=List[OutputItem])
def my_function(input: List[InputItem]): 
    df = pd.DataFrame(jsonable_encoder(input))
    result = df.apply(another_method)
    return result.to_dict(orient=records)

My question is, if I pass it a list like this:

[
  {"NOTFeature":"value"},
  {"Feature":"value"},
  {"Feature":"value"}
]

or if one of the values is of a different data type, the whole thing currently fails and returns an error. Is there a way to get it to handle the error so that the failing entry is skipped, and the API function is still carried out for items in the list which do pass validation?

Incidentally, if there's a smoother way to handle the dataframe conversion which still uses the dataframe (these are essential for the other data handling done in the functions), this would be very helpful to know as well!

2

There are 2 answers

0
Gustavo Kawamoto On BEST ANSWER

welcome to Stack Overflow.

The short answer for your question is no. That's because it's not pydantic (and also FastAPI) responsability to handle payload contents or fix malformed payloads.

The right way you could do that is to make the Feature member Optional and filter out when it gets to your method, something like this:

import fastapi
import typing
import pydantic

class InputItem(pydantic.BaseModel):
    feature: typing.Optional[str]

class OutputItem(pydantic.BaseModel):
    Feature: str
    Result: str

app = fastapi.FastAPI()

@app.post("/", response_model=typing.List[OutputItem])
def my_function(data: typing.List[InputItem]):
    data = [i for i in data if i.feature is not None]
    print(data)
    # ... do what you gotta do
0
Edd On

Union with dict as a passthrough seemed to work for me, i.e.:

from typing import List, Union

@app.post("/", response_model=List[OutputItem])
def my_function(input: List[Union[InputItem, dict]]): 
    df = pd.DataFrame(jsonable_encoder(input))
    result = df.apply(another_method)
    return result.to_dict(orient=records)