Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Obtain JSON from FastAPI using Pydantic Nested Models

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

like image 841
snow6oy Avatar asked Sep 05 '25 02:09

snow6oy


1 Answers

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))
like image 118
alex_noname Avatar answered Sep 07 '25 23:09

alex_noname