The following code receives some JSON that was POSTed to a FastAPI server. FastAPI makes it available within a function as a Pydantic model. My example code processes it by writing a file. What I don't like (and it seems to be side-effect of using Pydantic List) is that I have to loop back around to get some usable JSON.
How can I do this without looping?
I feel it must be possible because return images
just works.
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
import json
app = FastAPI()
class Image(BaseModel):
url: str
name: str
@app.post("/images/multiple/")
async def create_multiple_images(images: List[Image]):
#return images # returns json string
#print(images) # prints an Image object
#print(images.json()) # AttributeError: 'list' object has no attribute 'json'
#print(json.dumps(images)) # TypeError: Object of type Image is not JSON serializable
img_data = list() # does it really have to be this way?
for i in images:
img_data.append(i.dict())
with open('./images.json', 'w') as f:
json.dump(img_data, f, indent=2)
'''
curl -v -d '[{"name":"wilma","url":"http://this.com"},{"name":"barney","url":"http://that.com"}]' http://localhost:8000/images/multiple/
'''
The example is expanded from the FastAPI docs
To dump a list of model objects without loops, pydantic provides the ability to define a model with a custom root type.
Here is a small example of how it looks:
class Image(BaseModel):
url: str
name: str
class Images(BaseModel):
__root__: List[Image]
images_raw = '[{"url":"url1", "name":"name1"}, {"url":"url2", "name":"name2"}]'
images = parse_raw_as(Images, images_raw)
with open('./images.json', 'w') as f:
f.write(images.json(indent=2))
And the definition of your path operation would look like this:
@app.post("/images/multiple/")
async def create_multiple_images(images: Images):
with open('./images.json', 'w') as f:
f.write(images.json(indent=2))