FastAPI에 2개 이상의 DB연동하기
💡 Intro
- 원래 flask를 주로 사용하는데 지인분의 추천으로 FastAPI를 알게 되었습니다. FastAPI에서 libuv(node.js 성능의 핵심)를 코어로 사용하는 uvloop가 너무 매력적 이었고, ASGI를 한번 사용해보고싶어서 FastAPI를 한번 사용해볼까..? 라는 마음으로 사용해 보게 되었습니다.
- FastAPI로 관리자 웹을 만들다가 기존에 연결해 두었던 DB에 한개를 추가로 더 연결해서 MultipleDatabases를 구성해야하는 상황이 발생했습니다. 생각보다 reference가 많이 없었는데, FastAPI의 제작자이신 tiangolo님의 issues에서 이 문제를 찾을 수 있었습니다. Multipledatabases에 대해 다양한분들의 의견이 잘 정리되어있어서 이를 저의 FastAPI에 적용한것을 정리해보려고 합니다.🙌
🔎 그전에! FastAPI란?
공식 문서에는 다음과 같이 설명되어져 있습니다.
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints.
주요기능:
- Fast: NodeJS 및 Go와 비슷한 성능, 현존하는 파이썬 웹 프레임워크 중 가장 빠르다.
- Fast to code
- Fewer bugs
- Intuitive
- Easy
- Short: 적은버그와 코드중복을 최소화할 수 있고, 각 매개변수 선언에 여러기능을 제공해준다.
- Robust: 문서 자동화 및 쉬운 배포가 가능하다.
- Standards-based: 개방형 API 표준(OpenAPI&JSON)을 기반으로 한다.
이것만 아니라, 현재 아직 많은 레퍼런스가 나와있지는 않지만 그것을 매꿔줄 매우매우 수준높은 공식문서가 잘 마련되어져 있었습니다. 또한 백엔드 엔지니어 입장에서 API를 사용할 수 있도록 만든 문서 작업이 생각보다 많은 시간이 소요되는데, FastAPI는 문서의 자동화를 제공해줌으로써 개발자가 문서 작업에 할애하는 시간을 줄이고 오직 코드에만 집중할 수 있도록 해주어서 업무 효율을 증진 시켜줄 수 있습니다.
📚 Multiple databases 설정하기
📕 1. settings.py
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import os
from functools import lru_cache
from pydantic import BaseSettings
_VERSION_ = "0.1.0"
###multiple databases-1
class ISettings(BaseSettings):
db_engine: str = "mysql+pymysql"
db_host: str = ""
db_user: str = "root"
db_password: str = ""
db_port: int = 3306
db_database: str = ""
debug: bool = True
kakao_rest_key = ""
@property
def db_dsn(self):
dsn = f"{self.db_engine}://{self.db_user}:{self.db_password}@{self.db_host}:{self.db_port}/{self.db_database}"
return dsn
class DevelopmentSettings(ISettings):
pass
class ProductionSettings(ISettings):
debug = False
###multiple databases-2
class ISettings2(BaseSettings):
db_engine: str = "mysql+pymysql"
db_host: str = ""
db_user: str = "admin"
db_password: str = ""
db_port: int = 3306
db_database: str = ""
debug: bool = True
kakao_rest_key = ""
@property
def db_dsn(self):
dsn = f"{self.db_engine}://{self.db_user}:{self.db_password}@{self.db_host}:{self.db_port}/{self.db_database}"
return dsn
class DevelopmentSettings2(ISettings2):
pass
class ProductionSettings2(ISettings2):
debug = False
@lru_cache()
def get_settings():
config = os.environ.get("FASTAPI_CONFIG", "default")
configs = {
"development": DevelopmentSettings,
"production": ProductionSettings,
"default": DevelopmentSettings,
}
return configs.get(config, DevelopmentSettings)()
@lru_cache()
def get_settings2():
config = os.environ.get("FASTAPI_CONFIG", "default")
configs = {
"development": DevelopmentSettings2,
"production": ProductionSettings2,
"default": DevelopmentSettings2,
}
return configs.get(config, DevelopmentSettings2)()
📌 functools - @lru_cache** : LRU(Least Recently Used)캐싱을 사용하기위해 @lru_cache를 사용했습니다. @lru_cache 데코레이터는 functools 내장 모듈로 부터 불러올 수 있으며, @lru_cache 를 아무 함수 위에 선언하면 사용하면 그 함수에 넘어온 인자를 키(key)로 함수의 호출 결과를 값(value)으로 LRU캐싱이 적용됩니다.
📘 2. database.py
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
32
33
34
35
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from api.settings import get_settings, get_settings2
Base_1 = declarative_base()
Base_2 = declarative_base()
SQLALCHEMY_DATABASE_URL = get_settings().db_dsn
SQLALCHEMY_DATABASE_URL2 = get_settings2().db_dsn
###multiple databases-1
engine1 = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal1 = sessionmaker(autocommit=False, autoflush=False, bind=engine1)
###multiple databases-2
engine2 = create_engine(SQLALCHEMY_DATABASE_URL2)
SessionLocal2 = sessionmaker(autocommit=False, autoflush=False, bind=engine2)
def get_db():
session = SessionLocal1()
try:
yield session
session.commit()
finally:
session.close()
def get_db2():
session = SessionLocal2()
try:
yield session
session.commit()
finally:
session.close()
📌 sqlalchemy : SQL 문법 없이 개발 중인 언어로 데이터베이스에 접근할 수 있게 해주는 라이브러리를 ORM(Object Relational Mapping) 이라고 합니다. 그 중에서도 sqlalchemy은 python의 대표적인 ORM입니다.
📒 3. model.py
1
2
3
4
5
6
7
8
from api.database import Base_1, Base_2
from api.models.base_model import ModelBase
class DB_Schema1(Base_1, ModelBase):
__tablename__ = "DB_Schema1_table"
class DB_Schema2(Base_2, ModelBase):
__tablename__ = "DB_Schema2_table"
📗 4. route.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from fastapi import APIRouter, Depends, Body
from api.database import get_db, get_db2
router = APIRouter()
@router.get(
"/items/get_db1", description="multiple DB"
)
async def get_db1(
db=Depends(get_db),
):
return True
@router.get(
"/items/get_db2", description="multiple DB"
)
async def get_db2(
db=Depends(get_db2),
):
return True
[참고자료]