App Structure
테스트가 잘 수행 되었면 앱을 리팩터링하고 FastAPI의 APIRouter, 새로운 데이터베이스 초기화 함수 및 pydantic 모델을 추가해 보겠습니다.
API라우터
먼저 "app" 폴더에 "api"라는 새 폴더를 추가합니다. 새로 생성된 폴더에 __init__.py 파일을 추가합니다 .
이제 경로를 project/app/api/ping.py/ping 라는 새 파일로 이동할 수 있습니다 .
# project/app/api/ping.py
from fastapi import APIRouter, Depends
from app.config import get_settings, Settings
router = APIRouter()
@router.get("/ping")
async def pong(settings: Settings = Depends(get_settings)):
return {
"ping": "pong!",
"environment": settings.environment,
"testing": settings.testing
}
그런 다음 main.py를 업데이트하여 이전 경로를 제거하고 라우터를 기본 앱에 연결한 다음 함수를 사용하여 새 앱을 초기화합니다.
# project/app/main.py
import os
from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise
from app.api import ping
def create_application() -> FastAPI:
application = FastAPI()
register_tortoise(
application,
db_url=os.environ.get("DATABASE_URL"),
modules={"models": ["app.models.tortoise"]},
generate_schemas=False,
add_exception_handlers=True,
)
application.include_router(ping.router)
return application
app = create_application()
http://localhost:8004/ping 및 http://localhost:8004/docs가 여전히 작동하는지 확인하세요 .
테스트 목적으로 FastAPI의 새 인스턴스를 생성하는 ` create_application` 함수를 사용하려면 project/tests/conftest.pytest_app 의 픽스처를 업데이트합니다 .
# project/tests/conftest.py
import os
import pytest
from starlette.testclient import TestClient
from app.main import create_application # updated
from app.config import get_settings, Settings
def get_settings_override():
return Settings(testing=1, database_url=os.environ.get("DATABASE_TEST_URL"))
@pytest.fixture(scope="module")
def test_app():
# set up
app = create_application() # new
app.dependency_overrides[get_settings] = get_settings_override
with TestClient(app) as test_client: # updated
# testing
yield test_client
# tear down
여전히 테스트가 통과되는지 확인해 봅니다.
$ docker-compose exec web python -m pytest
=============================== test session starts ===============================
platform linux -- Python 3.12.1, pytest-7.4.4, pluggy-1.3.0
rootdir: /usr/src/app
plugins: anyio-4.2.0
collected 1 item
tests/test_ping.py . [100%]
================================ 1 passed in 0.16s ================================
프로젝트 구조는 아래와 같습니다.
├── .gitignore
├── docker-compose.yml
└── project
├── .dockerignore
├── Dockerfile
├── app
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ └── ping.py
│ ├── config.py
│ ├── db.py
│ ├── main.py
│ └── models
│ ├── __init__.py
│ └── tortoise.py
├── db
│ ├── Dockerfile
│ └── create.sql
├── entrypoint.sh
├── migrations
│ └── models
│ └── 0_20211227001140_init.sql
├── pyproject.toml
├── requirements.txt
└── tests
├── __init__.py
├── conftest.py
└── test_ping.py
Database 초기화
register_tortoise 헬퍼함수가 있는 db.py를 수정합니다.
# project/app/db.py
import os
from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise
TORTOISE_ORM = {
"connections": {"default": os.environ.get("DATABASE_URL")},
"apps": {
"models": {
"models": ["app.models.tortoise", "aerich.models"],
"default_connection": "default",
},
},
}
def init_db(app: FastAPI) -> None:
register_tortoise(
app,
db_url=os.environ.get("DATABASE_URL"),
modules={"models": ["app.models.tortoise"]},
generate_schemas=False,
add_exception_handlers=True,
)
init_db 함수를 main.py로 임포트합니다.
# project/app/main.py
import logging
from fastapi import FastAPI
from app.api import ping
from app.db import init_db
log = logging.getLogger("uvicorn")
def create_application() -> FastAPI:
application = FastAPI()
application.include_router(ping.router)
return application
app = create_application()
@app.on_event("startup")
async def startup_event():
log.info("Starting up...")
init_db(app)
@app.on_event("shutdown")
async def shutdown_event():
log.info("Shutting down...")
테스트하려면 먼저 컨테이너와 볼륨을 종료합니다.
$ docker-compose down -v
그런 다음 컨테이너를 다시 가져옵니다.
$ docker-compose up -d --build
마이그레이션을 적용하지 않았으므로 데이터베이스에 테이블이 표시되지 않습니다.
$ docker-compose exec web-db psql -U postgres
psql (16.1)
Type "help" for help.
postgres=# \c web_dev
You are now connected to database "web_dev" as user "postgres".
web_dev=# \dt
Did not find any relations.
web_dev=# \q
속도가 느려질 수 있는 Aerich를 통한 마이그레이션을 적용하는 대신 최종 상태의 데이터베이스에 스키마를 적용하려는 경우가 있을 수 있습니다. 따라서 이를 처리하기 위해 db.py에 generate_schema 함수를 추가해 보겠습니다 .
# project/app/db.py
import logging # new
import os
from fastapi import FastAPI
from tortoise import Tortoise, run_async # new
from tortoise.contrib.fastapi import register_tortoise
log = logging.getLogger("uvicorn") # new
TORTOISE_ORM = {
"connections": {"default": os.environ.get("DATABASE_URL")},
"apps": {
"models": {
"models": ["app.models.tortoise", "aerich.models"],
"default_connection": "default",
},
},
}
def init_db(app: FastAPI) -> None:
register_tortoise(
app,
db_url=os.environ.get("DATABASE_URL"),
modules={"models": ["app.models.tortoise"]},
generate_schemas=False,
add_exception_handlers=True,
)
# new
async def generate_schema() -> None:
log.info("Initializing Tortoise...")
await Tortoise.init(
db_url=os.environ.get("DATABASE_URL"),
modules={"models": ["models.tortoise"]},
)
log.info("Generating database schema via Tortoise...")
await Tortoise.generate_schemas()
await Tortoise.close_connections()
# new
if __name__ == "__main__":
run_async(generate_schema())
Tortoise.init 함수 호출을 위해서 generate_schema 함수가 먼저 호출됩니다. 그리고 tortoise 설정이 된다음 스키마가 생성됩니다.
$ docker-compose exec web python app/db.py
스키마가 있는지 확인해 봅니다.
$ docker-compose exec web-db psql -U postgres
psql (16.1)
Type "help" for help.
postgres=# \c web_dev
You are now connected to database "web_dev" as user "postgres".
web_dev=# \dt
List of relations
Schema | Name | Type | Owner
--------+-------------+-------+----------
public | textsummary | table | postgres
(1 row)
web_dev=# \q
개발에서는 Aerich를 이용할 것이므로 다시 컨테이너 볼륨을 내려줍니다.
$ docker-compose down -v
다시 이미지를 빌드하고 컨테이너를 기동합니다.
$ docker-compose up -d --build
데이터베이스에 테이블이 없는지 확인합니다. 그런 다음 마이그레이션을 적용합니다.
$ docker-compose exec web aerich upgrade
aerich와 testsummary 테이블이 이제 존재하는지 확인합니다.
Pydantic Model
models 패키지 내에 pydantic 파일 안에 새로운 pydantic 모델을 만듭니다.
# project/app/models/pydantic.py
from pydantic import BaseModel
class SummaryPayloadSchema(BaseModel):
url: str
'프로그래밍 언어 > 파이썬' 카테고리의 다른 글
FastAPI를 이용한 TDD 개발 - 8 (0) | 2024.03.14 |
---|---|
FastAPI를 이용한 TDD 개발 - 6 (0) | 2024.03.14 |
FastAPI를 이용한 TDD 개발 - 4 (0) | 2024.03.14 |
FastAPI를 이용한 TDD 개발 - 3 (0) | 2024.03.12 |
FastAPI를 이용한 TDD 개발 - 2 (0) | 2024.03.11 |