11

I am using FastAPI to write a web service. It is good and fast.

FastAPI is using pydantic models to validate input and output data, everything is good but when I want to declare a nested model for array of jsons like below:

[
   {
      "name": "name1",
      "family": "family1"
   },
   {
      "name": "name2",
      "family": "family2"
   }
]

I get empty response.

I think there is a problem with my model which is:

class Test(BaseModel):
    name: str
    family: str
    class Config:
        orm_mode = True

class Tests(BaseModel):
    List[Test]
    class Config:
        orm_mode = True

So, my question is how should I write a model for array of jsons?

kamal
  • 240
  • 1
  • 2
  • 9
  • 1
    Notice you have "orm_mode = True" , most likely you are experiencing a problem with you ORM and not with your Pydantic models... can't say much more because you have not provided details or your ORM. – user368604 Apr 29 '20 at 12:17
  • If you are POSTing json data, fastapi will try to convert it automatically to a pydantic model. Otherwise, you may simply declare a field as an array, as you did in Tests. Have you tried removing the "class Config: orm_mode = True" piece of code? – lsabi Apr 30 '20 at 16:11

1 Answers1

15

Update (26/09/2020)

In Python 3.9 (not yet released), you can do the same as below but with the built-in list generic type (which is always in scope) rather than needing to import the capitalized List type from typing, e.g.

@app.get("/tests", response_model=list[Test])

The issue here is that you are trying to create a pydantic model where it is not needed. If you want to serialize/deserialize a list of objects, just wrap your singular model in a List[] from python's builtin typing module. There is no need to try to create a plural version of your object with a pydantic BaseModel (and as you can see, it does not work anyway).

With that said, the simplest way to do what you want is to just specify a List[Test] at any point where you need a list of Tests, e.g.

from typing import List

from fastapi import FastAPI
from pydantic import BaseModel


existing_tests = [
    {
        "name": "name1",
        "family": "family1"
    },
    {
        "name": "name2",
        "family": "family2"
    }
]


class Test(BaseModel):
    name: str
    family: str

    class Config:
        orm_mode = True


app = FastAPI()


@app.get("/tests", response_model=List[Test])
async def fetch_tests():
    return existing_tests


@app.post("/tests")
async def submit_tests(new_tests: List[Test]):
    print(new_tests)

But of course if you find yourself repeatedly (or only) specifying Test as a list, you can of course just assign this to a variable and then use that variable where needed, like so:

Tests = List[Test]

@app.get("/tests", response_model=Tests)
async def fetch_tests():
    return existing_tests

@app.post("/tests")
async def submit_tests(new_tests: Tests):
    print(new_tests)

I think the first option is probably slightly clearer in your code though, and unless you are specifying List[Test] many times, using a variable for this purpose is probably not worth the extra layer of indirection.

acnebs
  • 218
  • 3
  • 9