Description
Is it possible to use UploadFile in a Pydantic model? The FastAPI docs say “FastAPI's UploadFile inherits directly from Starlette's UploadFile, but adds some necessary parts to make it compatible with Pydantic and the other parts of FastAPI.”
from fastapi import FastAPI, File, UploadFile
from pydantic import BaseModel
class PydanticFile(BaseModel):
file: UploadFile = File(...)
app = FastAPI()
@app.post("/uploadfile/")
async def create_upload_file(file: PydanticFile):
return {"filename": file.filename}
However, UploadFile does not seem to be compatible with Pydantic. The code above – derived from the example in FastAPI's Request Files tutorial – raises a value error:
ValueError: Value not declarable with JSON Schema, field: file type=UploadFile required
I'm a FastAPI/Starlette noob so take what I'm saying with a grain of salt, but I don't think what you are trying to do really makes sense.
When you are specifying a UploadFile I believe you are telling FastAPI/Starlette that you will be providing data in a multipart form body. Pydantic is for describing the expected JSON format body. So trying to combine the two doesn't really make sense because you need to do one or the other.
I could be off base though ... you can wait for someone else to chime in.
Thanks a lot for your helpful comment. So I guess I'd have to explicitly separate the file from the JSON part of the multipart form body, as in:
class Properties(BaseModel):
language: str = None
author: str = None
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...),
properties: Properties):
return {"filename": file.filename}
This seems to be working, and maybe query parameters would ultimately make more sense here.
When it says "adds necessary parts to make it compatible with Pydantic", it doesn't mean with pydantic BaseModel instances. FastAPI makes deep use of a lot of pydantic internals during the request-handling process, and I believe the modifications to the type are to make it compatible with that. In particular, it is used in a way that doesn't require it to be able to live as a field on a BaseModel.
Generally, you should only use BaseModel instances in FastAPI when you know you want to parse the model contents from the json body of the request.
If you want to make use of UploadFile as an attribute of a dependency class you can, it just can't be a pydantic model. (You can look at how the auth dependencies are implemented for an example along those lines.) A plain-old 3.7 dataclass would probably work for this.
This seems to be working, and maybe query parameters would ultimately make more sense here.
I would go the more RESTful approach and use /author/{author name or id}/language/{language name or id}/file. Upload is kind of implied with the POST request. With that said, this discussion is no long Fast API based so we should probably discontinue that discussion here.
Good day and good luck ...
Thanks a lot @michaelschmit and @dmontagu.
Thanks for the help here everyone! :clap: :bow:
Thanks for reporting back and closing the issue @laeubli :+1:
Hello,
So, what is the best approach when working with request bodies (represented as Pydantic models) and UploadFiles in FastAPI? How could I send the request body through multipart/form-data?
I'm pretty sure I am missing concepts about general HTTP requests, but if someone has a quick answer It'd be awesome.
Thanks in advance.
Thanks a lot for your helpful comment. So I guess I'd have to explicitly separate the file from the JSON part of the multipart form body, as in:
class Properties(BaseModel): language: str = None author: str = None @app.post("/uploadfile/") async def create_upload_file(file: UploadFile = File(...), properties: Properties): return {"filename": file.filename}This seems to be working, and maybe query parameters would ultimately make more sense here.
Hello,
This is giving an validation Error (422)
Thanks a lot for your helpful comment. So I guess I'd have to explicitly separate the file from the JSON part of the multipart form body, as in:
class Properties(BaseModel): language: str = None author: str = None @app.post("/uploadfile/") async def create_upload_file(file: UploadFile = File(...), properties: Properties): return {"filename": file.filename}This seems to be working, and maybe query parameters would ultimately make more sense here.
Hello,
This is giving an validation Error (422)
That's right. That example is not valid.
@perezzini 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.
Thanks for replying back @tiangolo
And congrats on your great job in FastAPI!
Thanks a lot for your helpful comment. So I guess I'd have to explicitly separate the file from the JSON part of the multipart form body, as in:
class Properties(BaseModel): language: str = None author: str = None @app.post("/uploadfile/") async def create_upload_file(file: UploadFile = File(...), properties: Properties): return {"filename": file.filename}This seems to be working, and maybe query parameters would ultimately make more sense here.
Hello,
This is giving an validation Error (422)That's right. That example is not valid.
Hi, Did you find a solution to pass UploadFile and a custom PydanticModel to a Request? I' currently stuck with the same validation Error (422)
Hello everyone,
I would also like to find a solution for this, my use case will be to send documents to a ML model in production and make predictions over those. But the input could vary between document files such as .pdf, .word, etc. and also there is a possibility to have plane text within a json.
Ideally, I would love a query parameter named input to switch between these types, so I wouldn't have to declare different endpoints for json or documents. But at the moment I can't send both a UploadFile and a custom PydanticModel inside a single request.
Any help would be greatly! appreciated.
Best regards
Came here from Goggle, and i found this on SO which could help solve this.
Most helpful comment
Hello,
This is giving an validation Error (422)