0

I have a small web server:

# app.py
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    id: int
    name: str


@app.post("/items")
async def items_list(items: List[Item]):
    return items

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

and a python file that posts to the endpoint:

# req.py
import asyncio
from typing import List
import datetime as dt
import aiohttp

from app import Item


async def main():
    data = [
        Item(id=1, name='A').dict(),
        Item(id=2, name='B').dict()
    ]

    async with aiohttp.ClientSession() as session:
        async with session.post(
                'http://localhost:8000/items',
                json=data
        ) as response:
            print(f'response: {await response.json()}')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

It works, I can get an output like:

response: [{'id': 1, 'name': 'A'}, {'id': 2, 'name': 'B'}]

If I set data as (no .dict()):

data = [
            Item(id=1, name='A'),
            Item(id=2, name='B')
        ]

it doesn't work because Item is not json serializable. My goal is to post list of items to the fastapi endpoint.

This working example doesn't work if I extend Item as this:

class Item(BaseModel):
    id: int
    name: str
    created_at: dt.datetime

created_at is a datetime and even I em using Items(..).dict() it is not json serializable. Funny thing is that if I create an Item as:

Item(id=1, name='A',created_at=dt.datetime.utcnow()).json()

its json is perfect:

{"id": 1, "name": "A", "created_at": "2021-12-15T21:10:36.077435"}

but as aiohttp session.post(json...) uses non pydantic json encoder, Item is not json serializable.

I tried to create a new pydantic object:

class ItemsList(BaseModel):
    data: List[Item]

end set it as:

data = [
    Item(id=1, name='A', created_at=dt.datetime.utcnow()),
    Item(id=2, name='B', created_at=dt.datetime.utcnow())
]

data_list = ItemsList(data=data)

Again, pydantic is clever enough to produce proper json:

data_list.json()
{"data": [{"id": 1, "name": "A", "created_at": "2021-12-15T21:17:34.368555"}, {"id": 2, "name": "B", "created_at": "2021-12-15T21:17:34.368555"}]}

but I am not sure how to send such json using aiohttp post.

My question is: How to by using aiohttp post a list of pydantic objects that contain datetime property to fastapi endpoint? I would be satisfied with sending/receiving list of itmes ([Item, Item, ... Item])

user3225309
  • 1,183
  • 3
  • 15
  • 31

1 Answers1

2

One way to do this is simply pass your Pydantic JSON string as the raw request body:

        # Using the "data_list" Pydantic object in one of your examples.
        async with session.post(
                'http://localhost:8000/items',
                # Pass the JSON string as `data`.
                data=data_list.json(),
                # Manually set content-type.
                content_type="application/json"
        ) as response:
            print(f'response: {await response.json()}')

That way you bypass the automatic serialization that isn't compatible with Pydantic.

jkinkead
  • 4,101
  • 21
  • 33
  • I hoped that I will not need to introduce ItemsList class. I found a solution by sending row json that I have got as data_json = jsonable_encoder(data), so data=data_json. Thanks. – user3225309 Dec 22 '21 at 18:03