How to add multiple body params with fileupload in FastAPI? - python

I have a machine learning model deployed using FastAPI, but the issue is I need the model to take two-body parameters
app = FastAPI()
class Inputs(BaseModel):
industry: str = None
file: UploadFile = File(...)
#app.post("/predict")
async def predict(inputs: Inputs):
# params
industry = inputs.industry
file = inputs.file
### some code ###
return predicted value
When I tried to send the input parameters I am getting an error in postman, please see the pic below,

From the FastAPI discussion thread--(#657)
if you are receiving JSON data, with application/json, use normal Pydantic models.
This would be the most common way to communicate with an API.
If you are receiving a raw file, e.g. a picture or PDF file to store it in the server, then use UploadFile, it will be sent as form data (multipart/form-data).
If you need to receive some type of structured content that is not JSON but you want to validate in some way, for example, an Excel file, you would still have to upload it using UploadFile and do all the necessary validations in your code. You could use Pydantic in your own code for your validations, but there's no way for FastAPI to do it for you in that case.
So, in your case, the router should be as,
from fastapi import FastAPI, File, UploadFile, Form
app = FastAPI()
#app.post("/predict")
async def predict(
industry: str = Form(...),
file: UploadFile = File(...)
):
# rest of your logic
return {"industry": industry, "filename": file.filename}

Related

How to get information from post data using fastapi

My script looks like this
from pydantic import BaseModel
from fastapi import FastAPI
class Credentials(BaseModel):
url:str
username:str
password:str
#app.post("/"/credentials"")
async def post_credentials(conn: Required):
return conn
#app.get("/main")
async def receive_from_post():
I would like to know if there is a way to retrieve the data from "POST" in "GET"

Loginradius with fastapi

I am testing loginradius for authentication in a fastapi app but cant seem to get the redirect or modal form to work correctly. I have tried using the OAuth2PasswordBearer and OAuth2AuthorizationCodeBearer classes but neither worked as desired. I would like to be able to click the "Authorize" button and either get redirected to the loginradius login page https://<app_name>.hub.loginradius.com (and returned to the api page with the token from loginradius) or render the loginradius login/registration form instead of the fastapi OAuth2PasswordBearer form. example:
Instead of
Desired outcome
Small Code Snippet
"""Fastapi routes for all User related endpoints"""
from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordBearer
router = APIRouter()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# oauth2_scheme = OAuth2AuthorizationCodeBearer(
# tokenUrl="https://<app_name>.hub.loginradius.com",
# authorizationUrl="https://<app_name>.hub.loginradius.com")
#router.post("/me", response_model=None)
def create_me(token: str = Depends(oauth2_scheme)):
return {"token": token}
Is that possible?
Thanks

Reading json:api filters with fast api

I'm trying to move my rest api project from flask to FastAPI
on my get function I made it very easy to get all the filters and by using flask I used the command
filter_value = request.args.get(f'filter[{filter_name}]')
where filter_name changed between all the fields i have on my object and that's how i could read urls like
http://127.0.0.1:8000/beers?filter[isbn]=72533
but now when I moved to fast api I can't find a way to read url like this.
Naming parameters in such a way, in my opinion, is not good.
Despite that, here's a working example.
from fastapi import FastAPI, Request
app = FastAPI()
#app.get("/")
async def get(req: Request):
if "filter[filter]" in req.query_params:
return req.query_params["filter[filter]"]
return ""
You have to directly access the request and manipulate it, since it is not possible to declare it as a function's parameter due to it's name. Choosing a different naming convention would allow to define the parameter as in the example below
from fastapi import FastAPI, Request
app = FastAPI()
#app.get("/")
async def get(my_parameter=None):
if my_parameter is not None:
return my_parameter
return ""

FastAPI equivalent of Flask's request.form, for agnostic forms

I try to migrate from Flask to FastAPI, and I was wondering if there is something similar to Flask's:
payload = request.form.to_dict(flat=False)
payload = {key:payload[key][0] for key in payload}
for FastAPI.
Until now I've found only some hacks, were you still had to implement one-by-one all the form's arguments to a function:
from pydantic import BaseModel
class FormData(BaseModel):
alfa: str=Form(...)
vita: str=Form(...)
async def Home(request: Request, form_data:FormData)
This example is of course better in readability than the standard form handling:
async def Home(username: str = Form(...), something_else: str = Form(...)):
But still it's quite restricting, due to the necessary declaration of all form fields.
Is there any other more agnostic & elegant approach?
Thanks in advance & I apologize if this a trivial question I've failed to find through googling :)
You can get the underlying starlette request and use its request.form() method. It requires python-multipart to work:
from fastapi import FastAPI, Request
app = FastAPI()
#app.post("/example")
async def example(request: Request):
form_data = await request.form()
return form_data
Example of calling it:
C:\>curl -X POST "http://localhost:8000/example" -d "hello=there&another=value"
{"hello":"there","another":"value"}

How to create an OpenAPI schema for an UploadFile in FastAPI?

FastAPI automatically generates a schema in the OpenAPI spec for UploadFile parameters.
For example, this code:
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
#app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(..., description="The file")):
return {"filename": file.filename}
will generate this schema under components:schemas in the OpenAPI spec:
{
"Body_create_upload_file_uploadfile__post": {
"title": "Body_create_upload_file_uploadfile__post",
"required":["file"],
"type":"object",
"properties":{
"file": {"title": "File", "type": "string", "description": "The file","format":"binary"}
}
}
}
How can I explicitly specify the schema for UploadFiles (or at least its name)?
I have read FastAPIs docs and searched the issue tracker but found nothing.
I answered this over on FastAPI#1442, but just in case someone else stumbles upon this question here is a copy-and-paste from the post linked above:
After some investigation this is possible, but it requires some monkey patching. Using the example given here, the solution looks like so:
from fastapi import FastAPI, File, UploadFile
from typing import Callable
app = FastAPI()
#app.post("/files/")
async def create_file(file: bytes = File(...)):
return {"file_size": len(file)}
#app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
return {"filename": file.filename}
def update_schema_name(app: FastAPI, function: Callable, name: str) -> None:
"""
Updates the Pydantic schema name for a FastAPI function that takes
in a fastapi.UploadFile = File(...) or bytes = File(...).
This is a known issue that was reported on FastAPI#1442 in which
the schema for file upload routes were auto-generated with no
customization options. This renames the auto-generated schema to
something more useful and clear.
Args:
app: The FastAPI application to modify.
function: The function object to modify.
name: The new name of the schema.
"""
for route in app.routes:
if route.endpoint is function:
route.body_field.type_.__name__ = name
break
update_schema_name(app, create_file, "CreateFileSchema")
update_schema_name(app, create_upload_file, "CreateUploadSchema")
You can edit the OpenAPI schema itself. I prefer to just move these schemas to the path (since they are unique to each path anyway):
from fastapi import FastAPI, File, UploadFile
from fastapi.openapi.utils import get_openapi
app = FastAPI()
#app.post("/uploadfile/")
async def create_upload_file(file1: UploadFile = File(...), file2: UploadFile = File(...)):
pass
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Custom title",
version="2.5.0",
description="This is a very custom OpenAPI schema",
routes=app.routes,
)
# Move autogenerated Body_ schemas, see https://github.com/tiangolo/fastapi/issues/1442
for path in openapi_schema["paths"].values():
for method_data in path.values():
if "requestBody" in method_data:
for content_type, content in method_data["requestBody"]["content"].items():
if content_type == "multipart/form-data":
schema_name = content["schema"]["$ref"].lstrip("#/components/schemas/")
schema_data = openapi_schema["components"]["schemas"].pop(schema_name)
content["schema"] = schema_data
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi

Categories

Resources