콘텐츠로 이동

쿼리 매개변수 모델

연관된 쿼리 매개변수 그룹이 있다면 Pydantic 모델 을 사용해 선언할 수 있습니다.

이렇게 하면 여러 곳에서 모델을 재사용할 수 있을 뿐만 아니라, 매개변수에 대한 검증 및 메타데이터도 한 번에 선언할 수 있습니다. 😎

참고

이 기능은 FastAPI 버전 0.115.0부터 제공됩니다. 🤓

쿼리 매개변수와 Pydantic 모델

필요한 쿼리 매개변수Pydantic 모델 안에 선언한 다음, 모델을 Query로 선언합니다.

from typing import Annotated, Literal

from fastapi import FastAPI, Query
from pydantic import BaseModel, Field

app = FastAPI()


class FilterParams(BaseModel):
    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: list[str] = []


@app.get("/items/")
async def read_items(filter_query: Annotated[FilterParams, Query()]):
    return filter_query
🤓 Other versions and variants
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
from typing_extensions import Annotated, Literal

app = FastAPI()


class FilterParams(BaseModel):
    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: list[str] = []


@app.get("/items/")
async def read_items(filter_query: Annotated[FilterParams, Query()]):
    return filter_query
from typing import List

from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
from typing_extensions import Annotated, Literal

app = FastAPI()


class FilterParams(BaseModel):
    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: List[str] = []


@app.get("/items/")
async def read_items(filter_query: Annotated[FilterParams, Query()]):
    return filter_query

Tip

Prefer to use the Annotated version if possible.

from typing import Literal

from fastapi import FastAPI, Query
from pydantic import BaseModel, Field

app = FastAPI()


class FilterParams(BaseModel):
    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: list[str] = []


@app.get("/items/")
async def read_items(filter_query: FilterParams = Query()):
    return filter_query

Tip

Prefer to use the Annotated version if possible.

from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
from typing_extensions import Literal

app = FastAPI()


class FilterParams(BaseModel):
    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: list[str] = []


@app.get("/items/")
async def read_items(filter_query: FilterParams = Query()):
    return filter_query

Tip

Prefer to use the Annotated version if possible.

from typing import List

from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
from typing_extensions import Literal

app = FastAPI()


class FilterParams(BaseModel):
    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: List[str] = []


@app.get("/items/")
async def read_items(filter_query: FilterParams = Query()):
    return filter_query

FastAPI는 요청의 쿼리 매개변수에서 각 필드의 데이터를 추출해 정의한 Pydantic 모델로 제공합니다.

문서 확인하기

/docs 경로의 API 문서에서 매개변수를 확인할 수 있습니다.

추가 쿼리 매개변수 금지

몇몇의 특이한 경우에 (흔치 않지만), 허용할 쿼리 매개변수를 제한해야할 수 있습니다.

Pydantic 모델 설정에서 extra 필드를 forbid 로 설정할 수 있습니다.

from typing import Annotated, Literal

from fastapi import FastAPI, Query
from pydantic import BaseModel, Field

app = FastAPI()


class FilterParams(BaseModel):
    model_config = {"extra": "forbid"}

    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: list[str] = []


@app.get("/items/")
async def read_items(filter_query: Annotated[FilterParams, Query()]):
    return filter_query
🤓 Other versions and variants
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
from typing_extensions import Annotated, Literal

app = FastAPI()


class FilterParams(BaseModel):
    model_config = {"extra": "forbid"}

    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: list[str] = []


@app.get("/items/")
async def read_items(filter_query: Annotated[FilterParams, Query()]):
    return filter_query
from typing import List

from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
from typing_extensions import Annotated, Literal

app = FastAPI()


class FilterParams(BaseModel):
    model_config = {"extra": "forbid"}

    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: List[str] = []


@app.get("/items/")
async def read_items(filter_query: Annotated[FilterParams, Query()]):
    return filter_query

Tip

Prefer to use the Annotated version if possible.

from typing import Literal

from fastapi import FastAPI, Query
from pydantic import BaseModel, Field

app = FastAPI()


class FilterParams(BaseModel):
    model_config = {"extra": "forbid"}

    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: list[str] = []


@app.get("/items/")
async def read_items(filter_query: FilterParams = Query()):
    return filter_query

Tip

Prefer to use the Annotated version if possible.

from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
from typing_extensions import Literal

app = FastAPI()


class FilterParams(BaseModel):
    model_config = {"extra": "forbid"}

    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: list[str] = []


@app.get("/items/")
async def read_items(filter_query: FilterParams = Query()):
    return filter_query

Tip

Prefer to use the Annotated version if possible.

from typing import List

from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
from typing_extensions import Literal

app = FastAPI()


class FilterParams(BaseModel):
    model_config = {"extra": "forbid"}

    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    order_by: Literal["created_at", "updated_at"] = "created_at"
    tags: List[str] = []


@app.get("/items/")
async def read_items(filter_query: FilterParams = Query()):
    return filter_query

만약 클라이언트가 쿼리 매개변수로 추가적인 데이터를 보내려고 하면, 클라이언트는 에러 응답을 받게 됩니다.

예를 들어, 아래와 같이 만약 클라이언트가 tool 쿼리 매개변수에 plumbus 라는 값을 추가해서 보내려고 하면,

https://example.com/items/?limit=10&tool=plumbus

클라이언트는 쿼리 매개변수 tool 이 허용되지 않는다는 에러 응답을 받게 됩니다.

{
    "detail": [
        {
            "type": "extra_forbidden",
            "loc": ["query", "tool"],
            "msg": "Extra inputs are not permitted",
            "input": "plumbus"
        }
    ]
}

요약

FastAPI 에서 쿼리 매개변수 를 선언할 때 Pydantic 모델 을 사용할 수 있습니다. 😎

스포일러 경고: Pydantic 모델을 쿠키와 헤더에도 적용할 수 있습니다. 이에 대해서는 이후 튜토리얼에서 다룰 예정입니다. 🤫