Search code examples
pythonfastapipydantic

Pydantic model for array of jsons


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?


Solution

  • 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.