FastAPI 개발일지/개발하기

FastAPI 개발일지11 - database 연결하기

hccode0419 2024. 10. 15. 10:05

1. 세팅하기

 

database를 연결하기 위해서 먼저 models.py와 database.py를 작성할 것이다. 

 

models.py는 database에 만들어질 테이블의 양식을 관리하는 파일이고,

database.py는 database의 연결을 도와주는 파일이다.

 

먼저 anaconda를 킨 후 필요한 것을 설치한다.

pip install mysql-connector-python
conda install sqlalchemy

 

- SQLAlchemy는 Python에서 가장 널리 사용되는 ORM (Object Relational Mapper)이다.

 

conda install python-dotenv

 

- dotenv의 자세한 설명은 아래 링크에서 봐주세요!

https://hccode0419.tistory.com/entry/Tip2-dotenv

 

2. env

DB_HOST = 127.0.0.1
DB_PORT = 3306
DB_PASSWORD = 12345678
ALGORITHM = HS256
ITEM_DB_NAME = item

 

env에는 개발을 위한 설정을 해준다. 

env파일에는 API의 key와 secret key도 관리할 수 있다. 

 

env 파일은 보통 공개되면 위험하다. 그래서 깃허브에 올릴 때 gitignore를 통해 관리한다. 자세한 이야기는 위의 env링크에서 볼 수 있다.

 

그서 원래는 env를 보여주면 안 되지만... 저거로는 뭐... 로컬에서 작업하고 잃을 것이 없기 때문에 저 정도는 그냥 따라 하면 될 것 같다.

 

지금까지 계속 따라왔으면 아마 DB_PASSWORD 빼고는 다 똑같을 것이다. 

DB_PASSWORD는 개발일지5에서 설정한 비밀번호로 작성하면 된다.

 

3. database.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

from dotenv import load_dotenv
import os
load_dotenv()


# .env의 정보를 불러온다.
DB_HOST = os.environ.get("DB_HOST")
DB_PASSWORD = os.environ.get("DB_PASSWORD")
ITEM_DB_NAME = os.environ.get("ITEM_DB_NAME")
DB_PORT = os.environ.get("DB_PORT", 3306)

# 어떤 DB와 연결할지 정의한다.
SQLALCHEMY_DATABASE_URL_ITEM = f"mysql+mysqlconnector://root:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{ITEM_DB_NAME}"

# create_engine 함수는 데이터베이스와 연결을 설정할 수 있는 엔진을 생성한다.
# 이 엔진을 통해 데이터베이스와 상호작용할 수 있다.
item_engine = create_engine(SQLALCHEMY_DATABASE_URL_ITEM)

# sessionmaker는 SQLAlchemy에서 세션을 관리하기 위한 함수이다.
# 세션은 데이터베이스에 대한 트랜잭션을 관리하는 객체로, 데이터베이스와의 통신을 효율적으로 처리한다.
item_SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=item_engine)

# declarative_base는 SQLAlchemy에서 데이터베이스의 테이블과 매핑될 클래스를 정의할 때 사용하는 기본 클래스이다. 
item_Base = declarative_base()

def get_itemdb():
    db = item_SessionLocal()
    try:
        yield db
    finally:
        db.close()

 

4. models.py 

from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Boolean
from database import item_Base


# User 모델 정의
class Item(item_Base):
    __tablename__ = "item"

    item_id = Column(Integer, primary_key=True, index=True)
    item_name = Column(String(255), nullable=False)
    item_price = Column(Integer, nullable=False)
    amount = Column(Integer, nullable=False)
    create_at = Column(String(30), nullable=False)
    create_date = Column(DateTime, nullable=False)

 

쉽게말하면 DB에 저장될 양식을 정의하는 파일이다.

지금은 ITEM 뿐이지만, 추후 USER, HISTORY 등 다양한 모델이 정의될 것이다. 

5. main.py

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from item.item_router import router as item_router

from database import item_Base, item_engine
app = FastAPI()

origins = ["*"]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# DB 접근
item_Base.metadata.create_all(bind=item_engine)

app.include_router(item_router, tags=["item"])

if __name__ == "__main__":
    import uvicorn

    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)

 

추가된 부분은 item_Base.metadata.create_all(bind=item_engine)이다.

uvicorn을 main에서 실행시키기 때문에 DB에 접근할 수 있도록 돕는다.

6. item

6-1 item/item_router.py

from fastapi import APIRouter, HTTPException, Depends, Response,Security

from sqlalchemy.orm import Session
from database import get_itemdb

from .item_schema import Create_item
from models import Item as Item_model

router = APIRouter(
    prefix="/item"
)


def insert_data(db, table):
    db.add(table)
    db.commit()
    db.refresh(table)

items = [
    {"item_id": 1, "item_name":"untoc", "item_price":15000},
    {"item_id": 2, "item_name":"phone", "item_price":35000},
    {"item_id": 3, "item_name":"computer", "item_price":24000},
    {"item_id": 4, "item_name":"pencil", "item_price":1000},
    {"item_id": 5, "item_name":"mouse", "item_price":2000},
    {"item_id": 6, "item_name":"water", "item_price":100}
]

@router.get("/")
def root():
    return {"message":"hello UNTOC"}

@router.get("/get_items")
def get_items(skip:int = 0, limit:int = 10):
    return items[skip : skip + limit]

@router.get("/get_item")
def get_item(item_id:int):
    for item in items:
        if item["item_id"] == item_id:
            return item
    return {"error": "Item not found"}


@router.post("/create_item", response_model=Create_item)
def create_item(item:Create_item, 
                item_db: Session = Depends(get_itemdb)):
    
    create_items = Item_model(item_name=item.item_name, 
                item_price=item.item_price,
                amount=item.amount,
                create_at=item.create_at,
                create_date=item.create_date)

    insert_data(item_db, create_items)

    return create_items

@router.put("/update_item/{item_id}")
def update_item(item_id: int, item_name: str, item_price: int):
    for item in items:
        if item["item_id"] == item_id:
            item["item_name"] = item_name
            item["item_price"] = item_price
            return item
    
    return {"error": "Item not found"}

@router.delete("/delete_item/{item_id}")
def delete_item(item_id: int):
    for item in items:
        if item["item_id"] == item_id:
            deleted_item = items.pop(item_id-1)
            return {"message": "deleted", "deleted_item":deleted_item}
    return {"error": "Item not found"}

 

코드는 길지만 지금 사용하는 부분은 create_item이다.

중요한 부분만 아래에서 보자.

# item/item_router.py 중 일부분
def insert_data(db, table):
    db.add(table)
    db.commit()
    db.refresh(table)
    
    
@router.post("/create_item", response_model=Create_item)
def create_item(item:Create_item, 
                item_db: Session = Depends(get_itemdb)):
    
    create_items = Item_model(item_name=item.item_name, 
                item_price=item.item_price,
                amount=item.amount,
                create_at=item.create_at,
                create_date=item.create_date)

    insert_data(item_db, create_items)

    return create_items

create_item 함수에는 item이라는 변수에 request body양식을 받아오고, item_db로 의존성을 주입하여 DB를 들고왔다.

 

create_items변수를 통해 Item_model에 맞게 정의한 후 그 모델에 맞게 insert_data 함수를 통해 DB에 데이터를 추가했다. 

 

 

6-2) item/item_schema.py

from pydantic import BaseModel
from datetime import datetime

# 입력 받는 양식
class create_item(BaseModel):
    item_name: str
    item_price: int
    amount: int
    create_at: str
    create_date: datetime

7. MySQL Workbench

 

MySQLWorkbench에 접속하여 5장에서 만든 UNTOC_shop에 connection 한다.

혹시라도 안되어있으면 https://hccode0419.tistory.com/8의 밑에 부분을 참고하여 만들어주세요!

 

7-1) database생성

- 왼쪽 위의 아이콘을 클릭한다.

- Name 부분에 Database의 이름을 작성한다. 이름은 env의 DB_NAME과 같은 이름으로 작성해야 한다.

- apply를 클릭하여 적용한다.

 

다음과 같이 item이라는 database를 생성하였다.

7-2) Table 생성

 

Table 생성은 간단하다.

models.py에 작성한 양식을 바탕으로 table이 생성되기 때문에, 연결만 제대로 됐으면 코드를 실행하면 자동으로 table을 생성한다. 

 

 

 

fastAPI docs에서 create_item을 실행시켜 보면 아이템이 DB로 들어가는 것을 확인할 수 있다. 

 

 

이제부터가 진짜 개발의 시작이다. 

 

다음 포스트부터는 DB를 통해서 data를 모두 관리할 것이다.