Как я могу принимать объекты на одном маршруте с массивомотдельными элементами с помощью pydantic/fastapi?

#python #rest #swagger #fastapi #variant

Вопрос:

Скажи, что у меня есть:

 from pydantic import BaseModel
from typing import Optional, List

class Create(BaseModel):
    code: List[str] = []
    args: List[str] = []
 

Завернутый во что-то вроде

 @router.post('/new', status_code=201)
async def create_project(data: Create):
    pass
 

Итак, это code и/или args является массивом или единственным значением для запрашивающего?

Таким образом, запрос на одиночный маршрут может содержать что-либо из этого:

 {code: "code", args: "arg"}
{code: ["code"], args: "arg"}
{code: ["code"], args: ["arg"]}
{code: "code", args: ["arg"]}
 

И всегда вызывайте обработчик с реальным типом, который использует списки?

Ответ №1:

Модели Pydantic принимают Union для определения поля, например:

 from typing import List, Union

class Create(BaseModel):
    code: Union[str, List[str]] = []
    args: Union[str, List[str]] = []
 

В этом случае оба code и args будут принимать либо a str , либо список/массив str s.

Оба code и args по умолчанию будут пустыми list .

Остальная часть кода остается прежней, например:

 from fastapi import FastAPI, status

app = FastAPI()

@app.post("/create", status_code=status.HTTP_201_CREATED)
async def create_project(data: Create):
    return data
 

Вызов конечной точки с помощью одного элемента:

 $ curl -i -X 'POST' 'http://127.0.0.1:8000/create' 
    -H 'accept: application/json' 
    -H 'Content-Type: application/json' 
    -d '{"code": "code", "args": "arg"}'

HTTP/1.1 201 Created
date: Sun, 29 Aug 2021 10:25:57 GMT
server: uvicorn
content-length: 28
content-type: application/json

{"code":"code","args":"arg"}
 

Вызов конечной точки с несколькими элементами:

 $ curl -i -X 'POST' 'http://127.0.0.1:8000/create' 
    -H 'accept: application/json' 
    -H 'Content-Type: application/json' 
    -d '{"code": ["code"], "args": ["arg"]}'

HTTP/1.1 201 Created
date: Sun, 29 Aug 2021 10:27:03 GMT
server: uvicorn
content-length: 32
content-type: application/json

{"code":["code"],"args":["arg"]}
 

Ответ №2:

У вас есть много возможностей, одна из универсальных-использовать валидатор (или корневой валидатор) и в нем анализировать отдельные значения в список. Вот так:

 class Create(BaseModel):
    code: List[str] = []

    @validator('code', pre=True)
    def code_validate(cls, values):
        if not isinstance(values, list):
            values = [values]
        return values
 

Комментарии:

1. К сожалению, я получаю 422 необработанных объекта из fastapi даже после применения этого с allow_reuse=True

2. В этом случае необходим минимальный воспроизводимый пример