Skip to content

26. Classes as Dependencies


1. Classes as Dependencies

  • Dependency Injection 시스템에 대해 더 자세히 알아보기 전에, 이전의 예제를 수정해 보자.


2. A dict from the previous example

  • 이전의 예제에서 dependency는 dict를 반환했다.


from typing import Optional
from fastapi import FastAPI, Depends

app = FastAPI()

async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}

@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons

@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return commons


  • 이와 같은 식으로 dict를 반환하는 경우, vscode와 같은 에디터는 키와 값의 타입을 알 수 없기 때문에 dict에 대한 자동 완성 등의 지원을 제공할 수 없게 되어 불편하다.


3. What makes a dependency

  • 지금까지는 dependency를 함수로 선언했다.
  • 하지만 이렇게 하는 방법이 dependency를 선언하는 유일한 것은 아니다.
  • dependency를 사용함에 있어서 핵심 요소는 dependency는 "callable"이어야 한다는 것이다.
  • 여기서 "callable"이란 Python이 호출("call")할 수 있는 객체를 의미한다.


  • 즉, 다음과 같이 호출할 수 있는 것을 callable이라고 한다.


something()


  • 또는 다음과 같다.


something(some_argument, some_keyword_argument="foo")


4. Classes as dependencies

  • Python에서 클래스의 인스턴스를 생성하기 위해서는 다음과 같은 구문을 사용한다.


class Cat:
    def __init__(self, name: str):
        self.name = name

fluffy = Cat(name="Mr Fluffy")


  • fluffyCat 클래스의 인스턴스이다.
  • 그리고 fluffy를 생성하기 위해서는 Cat을 호출해야 한다.
  • 함수와 마찬가지로 클래스 또한 callable이다.
  • 그렇기 때문에 FastAPI에서 클래스를 dependency로 사용할 수 있다.
  • FastAPI에서 callable을 dependency로 전달하면, FastAPI는 해당 callable에 대한 매개변수를 분석하고 path operation function에 대한 매개변수와 동일한 방식으로 처리한다.
  • 매개변수가 없는 callable 또한 적용된다.


  • 다음의 예제는 dependency를 common_parameters 함수에서CommonQueryParams 클래스로 변경하는 예제이다.


from typing import ItemsView, Optional
from fastapi import FastAPI, Depends, responses

app = FastAPI()

fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

class CommonQueryParams:
    def __init__(self, q: Optional[str] = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit

@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
    response = {}

    if commons.q:
        response.update({"q": commons.q})

    items = fake_items_db[commons.skip : commons.skip + commons.limit]
    response.update({"items": items})

    return response


5. Use it

  • 클래스로 dependency를 선언할 수 있다.


from typing import ItemsView, Optional
from fastapi import FastAPI, Depends, responses

app = FastAPI()

fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

class CommonQueryParams:
    def __init__(self, q: Optional[str] = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit

@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
    response = {}

    if commons.q:
        response.update({"q": commons.q})

    items = fake_items_db[commons.skip : commons.skip + commons.limit]
    response.update({"items": items})

    return response


  • FastAPI는 CommonQueryParams 클래스를 호출한다.
  • 이렇게 하면 해당 클래스의 인스턴스가 생성되고, 해당 인스턴스는 함수에 공통 매개변수로 전달된다.


6. Type annotation vs Depends

  • 위의 예제를 다시 보면 다음과 같이 CommonQueryParams를 두 번 작성했다.


commons: CommonQueryParams = Depends(CommonQueryParams)


  • Depends(CommonQueryParams)는 바로, FastAPI가 실제 dependency로 사용되는 것이 무엇인지 알기 위한 것이다.
  • 하지만 commons: CommonQueryParams는 데이터 변환이나 유효성 검사 등에 사용되는 것은 아니다.


  • 그러므로 다음과 같이 작성할 수도 있다.


commons = Depends(CommonQueryParams)


  • 하지만 타입을 선언하면 에디터가 매개변수 commons로 전달될 내용을 미리 알고 자동 완성 등을 지원할 수 있으므로 생략하지 않는 것이 좋다.

References