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(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")
fluffy
는 Cat
클래스의 인스턴스이다.
- 그리고
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