I just spent a lot of time trying to get this to work:
app.post("/post-form-here")
def process_this_form(form_data: Dict[str, str] = Form(...)):
# this does not work
pass
The documentation needs to be updated to make clear this will not work. At first it looks like you should be able to combine Form Data with Bodies of Arbitrary dicts. I could be wrong but I think there's no way to do this in FastAPI. If you are using Form() you have to explicitly define every form field one by one no matter how many fields you have, like so:
app.post("/post-form-here")
def process_this_form(
parameter_a: str = Form(...),
parameter_b: str = Form(...),
parameter_c: str = Form(...),
parameter_d: str = Form(...),
parameter_e: str = Form(...),
parameter_f: str = Form(...),
parameter_g: str = Form(...),
parameter_h: str = Form(...),
parameter_i: str = Form(...),
parameter_j: str = Form(...),
parameter_k: str = Form(...),
parameter_l: str = Form(...),
parameter_m: str = Form(...),
parameter_n: str = Form(...),
parameter_o: str = Form(...),
parameter_p: str = Form(...),
parameter_q: str = Form(...)):
# this works
pass
And you cannot have any arbitrary Form() fields either.
The solution I would like would be either to make this work like in Flask, where you have available request.form (a dict containing all x-www-form-urlencoded data.) Or else have the documentation explicitly state that you can't combine these two concepts Form Data with Bodies of Arbitrary dicts, and there is just no way to do it in FastAPI.
In my case I have 27 fields that go into a NamedTuple which goes into a table. To put 27 Form() parameters into one Python function def is going to be the longest def I've ever written, but I'd rather do that than waste a weekend beating my head against a wall.
I understand it may not be possible with the current design to read request.form like in Flask but please save people frustration by stating this up front in the documentation for Form().
@tfishr
Hi! I suppose that you are looking for request.form() method from the Starlette on which FastAPI is based. You can pass the Request object from Starlette as described here. This asynchronous method will return a dict-like object that can be used for your purposes:
from fastapi import FastAPI
from starlette.requests import Request
app = FastAPI()
@app.post("/my-form-endpoint")
async def my_endpoint(request: Request) -> dict:
form_data = await request.form()
# do something with form data
return form_data # just return passed data as json
Since the request.form() method is asynchronous, you cannot use it directly in your example with synchronous endpoint, but you can wrap receiving of your data from the form in a separate dependency and return either the data received from request.form() or the object that you create from this data:
from fastapi import FastAPI, Depends
from starlette.requests import Request
app = FastAPI()
async def parse_my_form(request: Request) -> dict:
form_data = await request.form()
return dict(form_data)
@app.post("/my-form-endpoint")
def my_endpoint(form_data: dict = Depends(parse_my_form)) -> dict:
return form_data
This is exactly what I was looking for. Thank you.
Now I can port my Flask code over to FastAPI a little more easily, and it only costs yet another import statement. I'm beginning to notice that as a pattern with FastAPI though.
from typing import Optional
from fastapi import FastAPI, Form
from pydantic import BaseModel
from starlette.staticfiles import StaticFiles
from starlette.requests import Request
from starlette.responses import RedirectResponse
from starlette.status import *
Thanks for the help here @nsidnev ! :cake: :bowing_man:
And thanks @tfishr for reporting back and closing the issue :+1:
Okay, I get this
But how can I do that reverse?
How can I unpack a dict or list or class of forms as parameters to a function
I just discovered Fast API and the thought of defining every form attribute in every function parameter has been keeping me from moving from Flask.
The way you can do...
```
class Person(BaseModel):
name: str = Field("name")
age: int = Field(18)
@app.route("/login")
def login(person: Person):
return person
````
For Body, how can I do something similar with Form, Cookie, Query to make them reusable?
Okay, I get this
But how can I do that reverse?
How can I unpack a dict or list or class of forms as parameters to a function
I just discovered Fast API and the thought of defining every form attribute in every function parameter has been keeping me from moving from Flask.
The way you can do...class Person(BaseModel): name: str = Field("name") age: int = Field(18) @app.route("/login") def login(person: Person): return personFor Body, how can I do something similar with Form, Cookie, Query to make them reusable?
Nevermind, I just reread this and read dependencies
I'm clear now, thanks
Most helpful comment
@tfishr
Hi! I suppose that you are looking for
request.form()method from theStarletteon whichFastAPIis based. You can pass theRequestobject fromStarletteas described here. This asynchronous method will return a dict-like object that can be used for your purposes:Since the
request.form()method is asynchronous, you cannot use it directly in your example with synchronous endpoint, but you can wrap receiving of your data from the form in a separate dependency and return either the data received fromrequest.form()or the object that you create from this data: