I'm working on a website where the frontend is done in React and the backend in Python with FastAPI. I made a form which takes a some data and sends it to the backend with axios. It looks like this
{
name='Jonathan',
aliases=["Johnny"],
birthdate='2-15-1980',
gender='male',
height=178
weight=90
nationalities=["American", "French"],
occupations=["Programmer", "Comedian"],
status='single',
images=[
{'attachment': FileList,
'location': 'Berlin',
'date': '10-14-2019'
}
]
}
However, when I submit it. FastApi seems to remove the images from the form.
name='Jonathan',
aliases=["Johnny"],
birthdate='2-15-1980',
gender='male',
height=178
weight=90
nationalities=["American", "French"],
occupations=["Programmer", "Comedian"],
status='single',
images=[
{'attachment': {'0': {}}, 'location': 'Berlin', 'date': '10-14-2019'}
]
This is what the route currently looks like
@router.post("/register/user")
def register_user(user_data: UserCreate):
print(user_data)
I'm not entirely sure what's going on. I'm guessing it has something to do with how the data is send and its encryption. I'm at a dead end here. Thanks in advance.
Edit: This is what the UserCreate Schema looks like
class CharacterCreate(BaseModel):
name: str
aliases: list
birthdate: Optional[str]
gender: str
height: Optional[float]
weight: Optional[float]
nationalities: Optional[set[str]]
occupations: Optional[set[str]]
status: str
images: Optional[list]
As per the documentation, you can't have both JSON and Files / form-data in the same request, as the body is encoded using multipart/form-data (when files are included) or application/x-www-form-urlencoded (if only Form data included) instead of application/json. Have a look at this answer.
Thus, one way to solve this is to use a single parameter to hold the various "metadata" and have a second one for the files/images. Using Dependency Injection you can check the received data against the Pydantic model using parse_raw method, before proceeding to the endpoint. If a ValidationError is thrown, then an HTTPException (HTTP_422_UNPROCESSABLE_ENTITY) should be raised, including the errors. Working example is given below. Alternatively, have a look at Method 4 in this answer.
app.py
from fastapi import FastAPI, Form, File, UploadFile, status
import pydantic
from pydantic import BaseModel
from typing import Optional, List
from fastapi.exceptions import HTTPException
from fastapi.encoders import jsonable_encoder
from fastapi import Depends
app = FastAPI()
class User(BaseModel):
name: str
aliases: List[str]
height: Optional[float]
weight: Optional[float]
def checker(data: str = Form(...)):
try:
user = User.parse_raw(data)
except pydantic.ValidationError as e:
raise HTTPException(detail=jsonable_encoder(e.errors()), status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)
return user
@app.post("/submit")
def submit(user: User = Depends(checker), files: List[UploadFile] = File(...)):
return {"User": user, "Uploaded Files": [file.filename for file in files]}
test.py
import requests
url = 'http://127.0.0.1:8000/submit'
files = [('files', open('test_files/a.txt', 'rb')), ('files', open('test_files/b.txt', 'rb'))]
data = {'data' : '{"name": "foo", "aliases": ["alias_1", "alias_2"], "height": 1.80, "weight": 80.5}'}
resp = requests.post(url=url, data=data, files=files)
print(resp.json())