-
DescriptionI started to use FastAPI and enjoyed serialisation of JSON bodies into pydantic models via type annotations and then I passed the form to my request handler and was surprised with So I re-read the docs about forms and found its behaviour inconsistent with JSON bodies even though probably all code might already support this. Examplefrom fastapi import FastAPI, Request, Form
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
another: str
@app.post("/item_form", response_model=Item)
async def find_item(r: Request):
# current workaround
form = await r.form()
item = Item(**form)
return item
@app.post("/item", response_model=Item)
async def find_item(r: Request, item: Item):
# JSON Body works nicely
print(item.name)
return item
@app.post("/item_form_a", response_model=Item)
async def find_item(item: Item = Form(...)):
# alternative 1
# current: 422 Unprocessable Entity
return item
class ItemForm(BaseModel):
name: str = Form(...)
another: str = Form(...)
@app.post("/item_form_b", response_model=ItemForm)
async def find_item(item: ItemForm):
# alternative 2
# current: AttributeError and 422 Unprocessable Entity
print(item.name)
return item MotivationI'm building a slack bot application which receives |
Beta Was this translation helpful? Give feedback.
Replies: 14 comments 3 replies
-
Please make sure you have installed the python-multipart library to enable form body support |
Beta Was this translation helpful? Give feedback.
-
@Mause I did, it did not change anything. I was talking to @ycd about this via gitter. |
Beta Was this translation helpful? Give feedback.
-
You can do something like this
or you can also use Depends()
Both of those look nice in the /docs as well 🙂 (something you won't get with Which you use is up to you, i prefer the first example since i'm not a fan of using Depends for too many stuff. |
Beta Was this translation helpful? Give feedback.
-
This is another possibility: def form_body(cls):
cls.__signature__ = cls.__signature__.replace(
parameters=[
arg.replace(default=Form(...))
for arg in cls.__signature__.parameters.values()
]
)
return cls
@form_body
class Item(BaseModel):
name: str
another: str |
Beta Was this translation helpful? Give feedback.
-
@ArcLightSlavik Thank you for providing examples @Mause Could you please provide more complete example? I'm not sure I used it right because I got an error
|
Beta Was this translation helpful? Give feedback.
-
Sorry, should have given a full example: from fastapi.testclient import TestClient
from fastapi import FastAPI, Depends, Form
from pydantic import BaseModel
app = FastAPI()
def form_body(cls):
cls.__signature__ = cls.__signature__.replace(
parameters=[
arg.replace(default=Form(...))
for arg in cls.__signature__.parameters.values()
]
)
return cls
@form_body
class Item(BaseModel):
name: str
another: str
@app.post('/test', response_model=Item)
def endpoint(item: Item = Depends(Item)):
return item
tc = TestClient(app)
r = tc.post('/test', data={'name': 'name', 'another': 'another'})
assert r.status_code == 200
assert r.json() == {'name': 'name', 'another': 'another'} |
Beta Was this translation helpful? Give feedback.
-
Thanks @Mause this is just perfect for my use-case! |
Beta Was this translation helpful? Give feedback.
-
@1oglop1 Could you close the issue |
Beta Was this translation helpful? Give feedback.
-
I took @Mause's idea (and some other add-ons I found Googling) and took it one step further. The current solutions didn't carry additional parameters like alias, description, example, and validation into the model / documentation. My solution is below. Any thoughts about potential issues or ways to further improve?
|
Beta Was this translation helpful? Give feedback.
-
@mjgraham123's idea worked perfectly in my use case with slight changes, that getattr from field.field_info instead of field, otherwise those attributes will be ignored.
and these are the libraries to be imported
|
Beta Was this translation helpful? Give feedback.
-
@Mause if I use Json as my type for a parameter in pydantic model I get error that "JSON object must be str, bytes or bytearray (type=type_error.json)". I have Form input field that shall pass json string as text. If I use in pydantic model its type as str things will work but am loosing that validate json feature. either you can alter your code above or guide on how to further use Depends to do that validation in the pydantic model. |
Beta Was this translation helpful? Give feedback.
-
I've implemented a simple library to use the pydantic schema for www-form-data FastAPI, hope you find it easier to work with :) Full support for validators, alias, example, regex, gt, ge and more... |
Beta Was this translation helpful? Give feedback.
-
I would really love to see this pyfa-converter library included upstream in fastapi - it solved my issue immediately. Thanks @dotX12 😁 Just from searching for ways to resolve this issue, I think it's a semi-common use case and it'd be great to see officially supported. 🎉 |
Beta Was this translation helpful? Give feedback.
-
Based on @Mause's implementation, I did the following: def form_body(cls):
"""."""
params = []
for arg in cls.__signature__.parameters.values():
arg_type = str(arg).split(':')[1]
new_arg = arg.replace(default=Form(...))
if 'Optional' in arg_type:
new_arg = arg.replace(default=Form(None))
params.append(new_arg)
cls.__signature__ = cls.__signature__.replace(parameters=params)
return cls And now the pydantic schema can have Optional parameters. |
Beta Was this translation helpful? Give feedback.
Sorry, should have given a full example: