from fastapi import FastAPI, Path, Query, HTTPException
from pydantic import BaseModel, Field
from typing import Optional
from starlette import status # status code를 사용하기 위한 import
app = FastAPI()
# Book 클래스 선언
class Book:
# 속성
id: int
title: str
author: str
description: str
rating: int
published_date: int
# 생성자
def __init__(self, id: int, title: str, author: str, description: str, rating: int, published_date: int):
self.id = id
self.title = title
self.author = author
self.description = description
self.rating = rating
self.published_date = published_date
# BookRequest 클래스로 책 생성 요청을 처리
class BookRequest(BaseModel):
id: Optional[int] = Field(description='ID is not needed on create', default=None) # ID는 생성 시 필요하지 않음
title: str = Field(min_length=3) # 제목은 최소 3자 이상
author: str = Field(min_length=1) # 저자는 최소 1자 이상
description: str = Field(min_length=1, max_length=100) # 설명은 최소 1자 이상, 최대 100자 이하
rating: int = Field(gt=0, lt=6) # 평점은 1~5 사이
published_date: int = Field(ge=2000, le=2030) # 출판년도는 2000~2030 사이
# 모델 설정
model_config = {
"json_schema_extra": {
"example": {
"title": "새로나온 책",
"author": "새로운작가",
"description": "새로운 책에 대한 설명",
"rating": 5,
'published_date': 2024
}
}
}
BOOKS = [
Book(id=1, title="Computer Science Pro", author="codingwithroby", description="A very nice book!", rating=5, published_date=2030),
Book(2, 'Be Fast with FastAPI', 'codingwithroby', 'A great book!', 5, 2030),
Book(3, 'Master Endpoints', 'codingwithroby', 'A awesome book!', 5, 2029),
Book(4, 'HP1', 'Author 1', 'Book Description', 2, 2028),
Book(5, 'HP2', 'Author 2', 'Book Description', 3, 2027),
Book(6, 'HP3', 'Author 3', 'Book Description', 1, 2026)
]
# 전체 책 목록을 반환하는 API
@app.get("/books", status_code=status.HTTP_200_OK) # status_code를 사용하여 200 OK 반환
async def read_all_books():
return BOOKS
# 책 ID를 받아서 해당 책을 반환하는 API
@app.get("/books/{book_id}", status_code=status.HTTP_200_OK) # status_code를 사용하여 200 OK 반환
async def read_book(book_id: int = Path(gt=0)): # book_id는 0보다 커야 함
for book in BOOKS:
if book.id == book_id:
return book
raise HTTPException(status_code=404, detail="책을 찾을 수 없습니다.") # 책을 찾을 수 없을 때 404 에러 반환
# 해당 rating을 가진 책 목록을 반환하는 API
@app.get("/books/", status_code=status.HTTP_200_OK) # /books/?rating=5 형태로 path parameter 사용
async def read_book_by_rating(book_rating: int = Query(gt=0, lt=6)): # rating은 1~5 사이어야 함
book_list = []
for book in BOOKS:
if book.rating == book_rating:
book_list.append(book)
return book_list
# 해당 출판년도를 가진 책 목록을 반환하는 API
@app.get("/books/publish/", status_code=status.HTTP_200_OK) # /books/publish/?publish_date=2029 형태로 path parameter 사용
async def read_book_by_publish_date(publish_date: int = Query(ge=2000, le=2030)):
book_list = []
for book in BOOKS:
if book.published_date == publish_date:
book_list.append(book)
return book_list
# 책을 생성하는 API
@app.post("/create-book", status_code=status.HTTP_201_CREATED) # status_code를 사용하여 201 Created 반환
async def create_book(book_request: BookRequest):
new_book = Book(**book_request.model_dump()) # model_dump() 함수는 모델의 속성을 딕셔너리로 변환
BOOKS.append(find_book_id(new_book))
# 책에 대한 id를 설정하는 함수
def find_book_id(book: Book):
book.id = 1 if len(BOOKS) == 0 else BOOKS[-1].id + 1
return book
# 책을 수정하는 API
@app.put("/books/update_book", status_code=status.HTTP_204_NO_CONTENT) # status_code를 사용하여 204 No Content 반환
async def update_book(book: BookRequest):
book_changed = False
for i in range(len(BOOKS)):
if BOOKS[i].id == book.id:
BOOKS[i] = book
book_changed = True
if not book_changed:
raise HTTPException(status_code=404, detail="해당 책을 수정할 수 없습니다.")
# 책을 삭제하는 API
@app.delete("/books/{book_id}", status_code=status.HTTP_204_NO_CONTENT) # status_code를 사용하여 204 No Content 반환
async def delete_book(book_id: int = Path(gt=0)):
book_changed = False
for i in range(len(BOOKS)):
if BOOKS[i].id == book_id:
del BOOKS[i] # 해당 책을 삭제 --> BOOKS.pop(i)로도 가능
book_changed = True
if not book_changed:
raise HTTPException(status_code=404, detail="해당 책을 삭제할 수 없습니다.")