Skip to content

Pass an object (or dict) in a form field. #2295

@TheTimKiely

Description

@TheTimKiely

Hi,

Description

I am looking to add an object or a dictionary to my endpoint.
There is an optional file in the request, so my understanding is that I need to use Form() fields.
Because there will be a lot of parameters to the endpoint, I would like to encapsulate related parameters in objects.
A simplified example would look like this:

class ThingDescription(BaseModel):
    name: str
    attribute1: str
    attribute2: str

@app.post("/things")
async def create_thing(request: Request, background_tasks: BackgroundTasks, name: str = Form(...),
                          thing_description: Optional[ThingDescription] = Form(None),
                          data_file: Optional[UploadFile] = File(None),
                          current_user=Security(get_current_user, scopes=[Scopes.build.name])):

I have tried requests using Swagger and the fastapi TestClient. Requests to this endpoint fail with validation errors.
I have tried to implement a Pydantic pre-validator, but it never gets called.
While troubleshooting, I used the debugger to break in fastapi dependencies\utils.py

v_, errors_ = field.validate(value, values, loc=loc)

I see that the Pydantic validator does not convert a string to a dict. As a test, I converted the string to a dict and the request succeeded and the object was populated in my fastapi function.

import ast
v_, errors_ = field.validate(ast.eval_literal(value), values, loc=loc)

This is where I hoped a Pydantic custom validator would help, but it doesn't seem to work as a pre-validator for the entire object.

My question is:

Is there a recommended way to do this?
Or, is it recommended that I simply list all those parameters on my endpoint?

Example

Here's a self-contained, minimal, reproducible, example with my use case:

from typing import Optional

from fastapi import FastAPI, Form, UploadFile, File
from pydantic import BaseModel

app = FastAPI()

class ThingDescription(BaseModel):
    name: str
    attribute1: str
    attribute2: str

@app.post("/things")
async def create_solution(name: str = Form(...),
                          thing_description: Optional[ThingDescription] = Form(None),
                          data_file: Optional[UploadFile] = File(None)):
    print(f'name: {name}')
    if thing_description:
        print(f'thing_description: {thing_description}')
    if data_file:
        print(f'filename: {data_file.filename}')
    return {'status': 'success'}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)
{
  "detail": [
    {
      "loc": [
        "body",
        "thing_description"
      ],
      "msg": "value is not a valid dict",
      "type": "type_error.dict"
    }
  ]
}

Environment

  • OS: Linux:
  • FastAPI Version [e.g. 0.61.1]:

Thanks,
Tim

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions