번역된 문장 관리 API

강태원·2023년 12월 17일
0

번역된 문장 관리

Database Table 생성

APP/
ㄴ models.py --> 작업할 공간

models.py

# 이전 상태에서 추가로 import 해줘야 할 라이브러리
from sqlalchemy.sql.schema import ForeignKey


class TsItem(BaseMin, Base):
    __tablename__ = "tsitem"

    dialect = Column(String(255), nullable=False)
    standard = Column(String(255), nullable=False)
    english = Column(String(255), nullable=False)
    chinese = Column(String(255), nullable=False)
    japanese = Column(String(255), nullable=False)
    owner_id = Column(Integer, ForeignKey("user.id"))

    owner = relationship("User", back_populates="items")
  1. dialect: 사투리
  2. standard: 표준어
  3. english: 영어
  4. chinese: 중국어
  5. japanse: 일본어
  6. owner_id: (외래키) 추가한 회원의 식별 ID

기능

controller.py

from fastapi import Depends, APIRouter, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm.session import Session
from typing import List

from app.database import get_db
from .schema import TsItemAdd, TsItem, TsItemDelete
from .service import addTsItem, findTsItems, deleteTsItem

router = APIRouter()
security = HTTPBearer()


# TsItem 테이블의 모든 row 로드
@router.get("", response_model=List[TsItem])
async def get_item_list(db: Session = Depends(get_db)):
    return await findTsItems(db)


# TsItem 테이블에 Item 추가
@router.post("", response_model=TsItem, status_code=status.HTTP_201_CREATED)
async def add_item(
    data: TsItemAdd,
    cred: HTTPAuthorizationCredentials = Depends(security),
    db: Session = Depends(get_db),
):
    return await addTsItem(data, cred, db)


# TsItem 테이블에서 Item 제거
@router.delete("/{id}", response_model=TsItemDelete, status_code=status.HTTP_202_ACCEPTED)
async def delete_item(
    id: int,
    cred: HTTPAuthorizationCredentials = Depends(security),
    db: Session = Depends(get_db),
):
    return await deleteTsItem(id, cred, db)

1. TsItem 조회

@router.get("", response_model=List[TsItem])
async def get_item_list(db: Session = Depends(get_db)):
    return await findTsItems(db)
  1. findtsItems 에서 받아온 정보를 TsItem으로 이루어진 List 형태에 맞춰 반환한다.
  2. 문제없이 작동했다면 반환할 때 200 상태 코드를 반환한다.

2. TsItem 생성

@router.post("", response_model=TsItem, status_code=status.HTTP_201_CREATED)
async def add_item(
    data: TsItemAdd,
    cred: HTTPAuthorizationCredentials = Depends(security),
    db: Session = Depends(get_db),
):
    return await addTsItem(data, cred, db)
  1. TsItemAdd 형태에 맞추어 Body 정보를 받는다.
  2. 로그인 할 때 받았던 토큰을 cred 인자로 받는다.
  3. addTsItem에서 받아온 정보를 TsItem 형식에 맞게 반환한다.
  4. 문제없이 작동했다면 반환할 때 201 상태 코드를 반환한다.

3. TsItem 삭제

@router.delete("/{id}", response_model=TsItemDelete, status_code=status.HTTP_202_ACCEPTED)
async def delete_item(
    id: int,
    cred: HTTPAuthorizationCredentials = Depends(security),
    db: Session = Depends(get_db),
):
    return await deleteTsItem(id, cred, db)
  1. Path 파라미터로 id 값을 받는다.
  2. 로그인 할 때 받았던 토큰을 cred 인자로 받는다.
  3. deleteTsItem에서 받아온 정보를 TsItem 형식에 맞게 반환한다.
  4. 문제없이 작동했다면 반환할 때 202 상태 코드를 반환한다.

service.py

from ..user import utils
from .utils import add_tsitem, find_tsitems, delete_tsitem


async def addTsItem(data, cred, db):
    decoded_dict = await utils.verify_user(cred)
    row = await add_tsitem(data, decoded_dict.get("id"), db)

    return row


async def findTsItems(db):
    return await find_tsitems(db)


async def deleteTsItem(id, cred, db):
    decoded_dict = await utils.verify_user(cred)
    return await delete_tsitem(id, decoded_dict.get("id"), db)

1. TsItem 추가

async def addTsItem(data, cred, db):
    decoded_dict = await utils.verify_user(cred)
    row = await add_tsitem(data, decoded_dict.get("id"), db)

    return row

user/utils.py 의 verify_user에서 반환되는 딕셔너리 객체에는 JWT 토큰을 인코딩할 때 사용되었던 정보들이 들어있다. ex) email, id

  1. verify_user 함수에 받아온 토큰 값을 넣어 해독된 딕셔너리를 받아온다.
  2. add_tsitem으로 사투리,표준어,...,일본어를 DB에 저장한다.
  3. 반환된 정보를 반환한다

2. TsItem들 반환

async def findTsItems(db):
    return await find_tsitems(db)
  1. find_tsitems 함수에서 반환된 정보를 반환한다.

3. TsItem 삭제

async def deleteTsItem(id, cred, db):
    decoded_dict = await utils.verify_user(cred)
    return await delete_tsitem(id, decoded_dict.get("id"), db)
  1. verify_user 함수에 받아온 토큰 값을 넣어 해독된 딕셔너리를 받아온다.
  2. delete_tsitem 함수로 TsItem 테이블에서 row를 삭제한 뒤 정보를 반환한다.

utils.py

from fastapi import HTTPException, status

from app.models import TsItem


async def add_tsitem(data, id, db):
    try:
        row = TsItem(**data.dict(), owner_id=id)
        db.add(row)
        db.commit()

        return row
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail=f"{e} occured while adding tsitem",
        )


async def find_tsitems(db):
    row = db.query(TsItem).all()

    return row


async def delete_tsitem(id, user_id, db):
    row = db.query(TsItem).filter_by(id=id).first()
    if row is None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No Item Found",
        )
    if row.owner_id == user_id:
        db.delete(row)
        db.commit()
    else:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="It's not owner")
        
    return row

1. TsItem 추가

async def add_tsitem(data, id, db):
    try:
        row = TsItem(**data.dict(), owner_id=id)
        db.add(row)
        db.commit()

        return row
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail=f"{e} occured while adding tsitem",
        )
  1. 넘어온 딕셔너리 객체를 풀어서 models.TsItem 객체를 생성한다. 여기서 owner_id는 외래키이다.
  2. DB의 TsItem 테이블에 추가한다.
  3. Commit

2. TsItem들 반환

async def find_tsitems(db):
    row = db.query(TsItem).all()

    return row

all() 메소드로 query를 사용하면 리스트 형태로 반환된다.

  1. TsItem 테이블의 모든 row를 반환한다.

3. TsItem 삭제

async def delete_tsitem(id, user_id, db):
    row = db.query(TsItem).filter_by(id=id).first()
    if row is None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No Item Found",
        )
    if row.owner_id == user_id:
        db.delete(row)
        db.commit()
    else:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="It's not owner")
        
    return row

sqlalchemy에서 row를 찾지못하면 None 값을 반환한다.

  1. Path 파라미터로 들어온 id로 TsItem 객체를 검색한다.
  2. row가 None이라면, 즉시 400 상태 코드를 반환한다.
  3. models.TsItem 객체의 owner_id(회원 식별 ID)와 토큰 해독 후 나온 ID가 일치하지 않는다면 403 상태 코드를 반환한다.
  4. 일치한다면 객체를 테이블에서 삭제하고 Commit

schema.py

from pydantic import BaseModel


class TsItemAdd(BaseModel):
    dialect: str
    standard: str
    english: str
    chinese: str
    japanese: str


class TsItem(TsItemAdd):
    id: int
    owner_id: int

    class Config:
        from_attributes = True


class TsItemDelete(BaseModel):
    id: int

생각해볼 점

생성한 Table에는 owner 라는 relationship으로 회원과 TsItem이 일대다 관계를 맺고 있지만, 위에 작성된 엔드포인트 중에서 relationship을 사용해서 query문을 작성한 적이 없다. 이는 모든 TsItem을 불러오는 엔드포인트만 존재하기 때문인데, 이 글을 읽고 있는 독자(있나?)가 있다면 이 부분을 생각해보면서 회원 별로 생성한 TsItem을 불러오는 엔드포인트를 작성해보면 좋을 것 같다.

[참고]

필자가 모든 TsItem을 불러오는 엔드포인트로 작성한 이유는, 캡스톤디자인 경연대회 당일날에 다른 사람들은 어떤 문장을 번역해봤을지 늦게 온 사람들도 확인해볼 수 있도록 하기 위해서였다.


위의 내용들을 잘 따라왔다면 사진처럼 Swagger에서 확인이 될 것이다.
이번에도 필자는 리팩토링 이 후 사진이라 V2로 태깅이 되어있다.

다음 게시글에서는 방명록 관련해서 작성하겠다.

profile
가치를 창출하는 개발자! 가 목표입니다

0개의 댓글