Replies: 11 comments
-
|
I don't think we can use a nested Pydantic model with |
Beta Was this translation helpful? Give feedback.
-
|
Hi all. |
Beta Was this translation helpful? Give feedback.
-
|
Hi! As described in documentation:
The workaround for this would be to send JSON as a string and parse it on your own. But I'm not sure if it's recommended. |
Beta Was this translation helpful? Give feedback.
-
Hi, I did see this in the docs but I was not certain of if that applied as in the same bit of docs it describes using Form() for JSON? Or maybe I am misunderstanding? |
Beta Was this translation helpful? Give feedback.
-
|
The only limitation here is you will not be able to have nested JSONs within form. from enum import Enum
from typing import Type
import inspect
from fastapi import (
FastAPI,
File,
UploadFile,
Form, Depends,
)
from pydantic import BaseModel, Field
app = FastAPI()
def as_form(cls: Type[BaseModel]):
new_parameters = []
for field_name, model_field in cls.__fields__.items():
model_field: ModelField # type: ignore
new_parameters.append(
inspect.Parameter(
model_field.alias,
inspect.Parameter.POSITIONAL_ONLY,
default=Form(...) if not model_field.required else Form(model_field.default),
annotation=model_field.outer_type_,
)
)
async def as_form_func(**data):
return cls(**data)
sig = inspect.signature(as_form_func)
sig = sig.replace(parameters=new_parameters)
as_form_func.__signature__ = sig # type: ignore
setattr(cls, 'as_form', as_form_func)
return cls
# Models
class ModelProcessor(str, Enum):
CPU = "cpu"
GPU = "gpu"
TPU = "tpu"
DEFAULT = CPU
class ModelThresholds(BaseModel):
confidence: float = Field(0.5, ge=0.0, le=1.0, descritpiton="Confidence Threshold")
nms: float = Field(
0.4, ge=0.0, le=1.0, description="Non-Maximum Suppression Threshold"
)
class ModelOptions(BaseModel):
processor: ModelProcessor = Form(None, description="Processor to use for model")
height: int = Form(
416, ge=1, description="Height of the input image (resized for model)"
)
width: int = Form(
416, ge=1, description="Width of the input image (resized for model)"
)
square: bool = Form(False, description="Zero pad the image to be a square")
fp_16: bool = Form(False, description="model uses Floating Point 16 Backend (EXPERIMENTAL!)")
# thresholds: ModelThresholds = Form(...)
@app.post("/detect/{model_uuid}", summary="Run detection on an image")
async def object_detection(
model_uuid: str,
model_options: ModelOptions = Depends(),
image: UploadFile = File(..., description="Image to run the ML model on"),
):
print(f"Running detection on image '{image.filename}'")
return {"filename": image.filename}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=5001)In the above code, I just commented out |
Beta Was this translation helpful? Give feedback.
-
|
@iudeen - I am just following what the docs say about uploading an image with extra data -> https://fastapi.tiangolo.com/tutorial/request-forms-and-files/ Perhaps I am misunderstanding though? |
Beta Was this translation helpful? Give feedback.
-
|
Examples in documentation have You can use @iudeen's solution, but you can't nest your models. @app.post("/detect/{model_uuid}", summary="Run detection on an image")
async def object_detection(
model_uuid: str,
json_as_str: Json = Form(), # from pydantic import Json
image: UploadFile = File(..., description="Image to run the ML model on"),
):
print(f"Running detection on image '{image.filename}'")
return {"filename": image.filename} |
Beta Was this translation helpful? Give feedback.
-
|
Thank you for that, I am just trying to understand my error. So Form() will only accept str input? pydantic Json will take JSON formatted as a str and load it into a python dict? Meaning I would need to instantiate a ModelOptions object using ModelOptions(**json_as_str) and then pass it to whatever function? My only concern thereafter would be the Swagger docs being incomplete on what the json_as_str should contain since it wont be able to reference ModelOptions. Maybe I can set ModelOptions as Query()? So the end user knows what the options and defaults are? Sorry if this seems obvious, I am just trying to understand. |
Beta Was this translation helpful? Give feedback.
-
Something like that. I haven't tested it, but I guess you can do something like: @app.post("/detect/{model_uuid}", summary="Run detection on an image")
async def object_detection(
model_uuid: str,
model_options: Json = Form(),
image: UploadFile = File(..., description="Image to run the ML model on"),
):
print(f"Running detection on image '{image.filename}'")
options = ModelOptions.parse_obj(model_options)
return {"filename": image.filename}and I can't provide answers for other questions, since I'm not sure. |
Beta Was this translation helpful? Give feedback.
-
|
Thank you for the help, I am testing a few things out with the supplied options. Appreciate it very much @iudeen and @mbroton! |
Beta Was this translation helpful? Give feedback.
-
|
@baudneo one last attempt to get as close as you want this to be. This handles everything. import json
from enum import Enum
from typing import Type, Dict, Optional
import inspect
from fastapi import (
FastAPI,
File,
UploadFile,
Form, Depends,
)
from dataclasses import dataclass, field
from pydantic import BaseModel
app = FastAPI()
# Models
class ModelProcessor(str, Enum):
CPU = "cpu"
GPU = "gpu"
TPU = "tpu"
DEFAULT = CPU
class ModelThresholds(BaseModel):
confidence: float = Form(0.5, ge=0.0, le=1.0, descritpiton="Confidence Threshold")
nms: float = Form(
0.4, ge=0.0, le=1.0, description="Non-Maximum Suppression Threshold"
)
@dataclass
class ModelOptions:
processor: ModelProcessor = Form(None, description="Processor to use for model")
height: int = Form(
416, ge=1, description="Height of the input image (resized for model)"
)
width: int = Form(
416, ge=1, description="Width of the input image (resized for model)"
)
square: bool = Form(False, description="Zero pad the image to be a square")
fp_16: bool = Form(False, description="model uses Floating Point 16 Backend (EXPERIMENTAL!)")
thresholds: Optional[str] = Form(ModelThresholds().json())
@app.post("/detect/{model_uuid}", summary="Run detection on an image")
async def object_detection(
model_uuid: str,
model_options: ModelOptions = Depends(),
image: UploadFile = File(..., description="Image to run the ML model on"),
):
threshold = ModelThresholds(**json.loads(model_options.thresholds))
print(threshold.dict())
print(f"Running detection on image '{image.filename}'")
return {"filename": image.filename}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=5001) |
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
-
First Check
Commit to Help
Example Code
Description
What I am doing
model_uuidfield and any image for theimage: UploadFile, themodel_optionsis prefilled with proper JSON based upon the BaseModel.What is happening
Error returned is 422: Unprocessable Entity ->
What I expect to happen
I expect that the return statement is returned with a 200 response code.
Operating System
Linux
Operating System Details
No response
FastAPI Version
0.79.0
Python Version
3.10.6
Additional Context
Beta Was this translation helpful? Give feedback.
All reactions