Post

FastAPI 공식문서 따라하기[11] - Body - Nested Models

https://fastapi.tiangolo.com/tutorial/body-nested-models/ 공식문서 따라하는 글

☑️ Body Nested Models

Pydantic를 이용한 FastAPI는 좀 더 복잡한 모델의 자료형에 대해 검증, 정의, 문서화 및 사용할 수 있게한다.

☑️ List fields

바로 코드예제로 보겠다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list = []


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

tags속성을 보면 리스트로 되어 있다.

☑️ List fields with type parameter

list에 타입을 명시하고 싶다면 어떻게 해야할까?

Python3.9 버전 ~ Python 3.6 버전에서는 Listtyping에서 임포트해야한다.

1
from typing import List, Union

이렇게 하면 list,dict,tuple에 자료형을 명시할 수 있다.

1
2
3
4
5
#python3.9 above
my_list: list[str]
#python3.6 ~ 3.9
from typing import List,Union
my_list: List[str]

이로써 자료구조에 타입을 명시할 수 있게 되었다.

☑️ Set types

데이터가 중복이 되면 안되는 유니크한 값이라면 set을 사용하고 싶을 것이다. 다행스럽게도 set을 지원하는데, 다음과 같이 쓰면 된다.

1
2
3
4
5
6
# Python 3.6 above
from typing import Set,Union
my_set: Set[str] = set()

# Python 3.9 above
my_set: set[str] = set()

☑️ Nested Models

Pydantic의 속성들은 데이터 타입이 있다.

Pydantic의 모델들은 다른 Pydantic의 속성값이 될 수 있다.

그렇기에 복잡하게 얽힌 JSON object들의 각 속성들도 잘 명시되어 있으면 데이터 검증을 통해서 다른 모델의 속성값이 될 수 있다.

예를들어서 이렇게 모델을 정의할 수 있을 것이다.

1
2
3
4
5
6
7
8
9
10
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Image(BaseModel):
    url: str
    name: str

BaseModel을 상속받은 Image는 다른 클래스에서 쓰일 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Image(BaseModel):
    url: str
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    #
    image: Image | None = None

Itemimage라는 속성은 Image객체를 받거나 None값을 받는다.

그러면 RequestBody에 이렇게 담아야 할 것이다.

{
    "name": "Foo",
    "description": "The pretender",
    "price": 42.0,
    "tax": 3.2,
    "tags": ["rock", "metal", "bar"],
    "image": {
        "url": "http://example.com/baz.jpg",
        "name": "The Foo live"
    }
}

☑️ Speical types and validation

str,int,float같은 일반적인 자료형 말고, 조금더 복잡한 자료형을 상속받은 str을 사용해보겠다.

위의 Image라는 모델에서 url이라는 속성을 str형태로 정의했는데, 이 대신에 Pydantic이 가지고 있는 HttpUrl을 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from fastapi import FastAPI
from pydantic import BaseModel,HttpUrl

app = FastAPI()


class Image(BaseModel):
    #
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    image: Image | None = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

저렇게 선언하면 Swagger와 같은 문서들이 URL의 형태인지 확인한다. 만약 아니라면

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "detail": [
    {
      "loc": [
        "body",
        "image",
        "url"
      ],
      "msg": "invalid or missing URL scheme",
      "type": "value_error.url.scheme"
    }
  ]
}

라며 422 에러를 발생시킨다.

☑️ Attributes with lists of submodels

listsetPydantic모델의 자료형으로 선언할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Image(BaseModel):
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    #
    images: list[Image] | None = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

이렇게 선언하면 RequestBody에 다음과 같이 데이터를 넘길 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
    "name": "Foo",
    "description": "The pretender",
    "price": 42.0,
    "tax": 3.2,
    "tags": [
        "rock",
        "metal",
        "bar"
    ],
    "images": [
        {
            "url": "http://example.com/baz.jpg",
            "name": "The Foo live"
        },
        {
            "url": "http://example.com/dave.jpg",
            "name": "The Baz"
        }
    ]
}

☑️ Deeply nested models¶

2중으로 중첩해서 모델을 담을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Image(BaseModel):
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    images: list[Image] | None = None


class Offer(BaseModel):
    name: str
    description: str | None = None
    price: float
    items: list[Item]


@app.post("/offers/")
async def create_offer(offer: Offer):
    return offer

코드를 보면 OfferItem객체를 리스트로 담고 있는데, Item객체는 Image를 리스트로 담고 있다.

☑️ Bodies of pure lists

요청함수 파라미터로 list형태를 받을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Image(BaseModel):
    url: HttpUrl
    name: str


@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):
    return images

데이터는 다음처럼 넣어서 확인하면 된다.

1
2
3
4
5
6
7
8
9
10
[
  {
    "url": "http://item1.com",
    "name": "item1"
  },
  {
    "url": "http://item2.com",
    "name": "item2"
  }
]

☑️ Bodies of arbitrary dicts¶

dict형태의 값도 요청함수 파라미터로 받을 수 있다.

1
2
3
4
5
6
7
8
9
from fastapi import FastAPI

app = FastAPI()


@app.post("/index-weights/")
async def create_index_weights(weights: dict[int, float]):
    return weights

1
2
3
4
{
"1" : 0.4,
"2" : 4.3
}

이런식으로 키값을 넣어주면 되는데, 의문점이 생길 것이다. key값을 str자료형으로 넘겼는데 에러가 뜨지 않는다.

JSON은 오로지 str형태의 키값을 가진다. Pydantic은 자동적으로 데이터 자료형을 변환시켜주는데 이로 인해서 str형태로 키를 넘겨도 이를 자동으로 integers로 변환해준다.

This post is licensed under CC BY 4.0 by the author.