FAST API 게시판 만들기

main.py

1. import

from fastapi import FastAPI, Form, Request, HTTPException
import uvicorn #서버
from typing_extensions import Annotated
from fastapi.responses import RedirectResponse
from datetime import datetime
from sqlalchemy import create_engine
from fastapi.templating import Jinja2Templates

2. DB커넥션 / template 폴더 마운트 / fastapi 인스턴스 생성

db_connection = create_engine("mysql+pymysql://test:1234@localhost/test")

templates = Jinja2Templates(directory = "templates")

app = FastAPI()

5. 게시판 리스트

@app.get("/")
def list_contents(request: Request):
    #게시판을 뿌려야하니까.
    #DB select 조회 구절이 필요해보임                # c_id 기준으로 내림차순
    query = db_connection.execute("select * from content order by c_id DESC")
    contents = query.fetchall()
    result = []
    for content in contents:
        temp = {'c_id': content[0],
                'c_title': content[1],
                'c_text': content[2],
                'user_id': content[3],
                'date': content[4]}
        result.append(temp)
    return templates.TemplateResponse("list.html", \
                                      context = {"request":request,'contents':result})

6-1. 게시판 작성

@app.get("/write")
def write_form(request: Request):
    return templates.TemplateResponse("write.html",\
                                      {'request':request})

6-2. 게시판 작성 처리

@app.post("/write")
def write_content(
    request: Request,
    title: Annotated[str,Form()],
    text: Annotated[str,Form()],
    user_id: Annotated[str,Form()]):

    # 현재 날짜 가져오기
    current_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    # 뭔가 DB insert 구문이 필요할 것 같다.
    db_connection.execute(
        "insert into content(c_title,c_text,user_id,date) values(%s,%s,%s,%s)",
        (title,text,user_id, current_date)
    )
   
                                    # 303: 현재 post여서 -> get 통신으로 시그널 줌
                                    # get 라우터인 "/" 함수를 쫓아감
    return RedirectResponse(url="/", status_code = 303)

7. 게시판 상세

@app.get("/content/{content_id}")
def content_detail(request: Request, content_id: int):
    query = db_connection.execute("select * \
                                    from content \
                                    where c_id = %s", (content_id,))
   
    content = query.fetchone() # 여러개는 fetchall
    from fastapi import HTTPException
    if content is None:
        raise HTTPException(status_code=404, detail="Content not found")
    result = {
        'c_id': content[0],
        'c_title': content[1],
        'c_text': content[2],
        'user_id': content[3],
        'date': content[4]
    }
    return templates.TemplateResponse("detail.html",\
                                      {"request": request, "content": result})

8. 게시판 삭제

@app.delete("/content/{content_id}")
async def delete_content(content_id: int):
   
    # 무턱대고 delete 날리는 게 아니라 존재 확인 후 삭제
    query = db_connection.execute("select *\
                                  from content \
                                  where c_id=%s",(content_id,))
    content = query.fetchone()

    if content is None:
        raise HTTPException(status_code = 404, detail = "Content not found")
    db_connection.execute("delete from content where c_id = %s", (content_id,))

    return {"message": "Content deleted successfully"}

if __name__ == "__main__":
  uvicorn.run(app, host = "localhost", port = 8000)

list.html

<style>
        table {
            width: 100%;
            border-collapse: collapse;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
        }
        th {
            background-color: #F2F2F2;
        }
        .write-btn {
            margin: 10px 0;
            padding: 5px 10px;
            background-color: #4CAF50;
            color: white;
            text-decoration: none;
            border-radius: 4px;
            display: inline-block;
        }
        .write-btn:hover {
            background-color: #45A049;
        }
    </style>
</head>
<body>
    <h1>게시판 목록</h1>
    <a href="/write" class="write-btn">글쓰기</a>
    <table>
        <thead>
            <tr>
                <th>번호</th>
                <th>제목</th>
                <th>작성자</th>
                <th>작성일</th>
            </tr>
        </thead>
        <tbody>
            {% for content in contents %}
            <tr>
                <td>{{ content.c_id }}</td>
                <td><a href="/content/{{ content.c_id }}">{{ content.c_title }}</a></td>
                <td>{{ content.user_id }}</td>
                <td>{{ content.date }}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</body>

detail.html

<!DOCTYPE html>
<html>
<head>
    <title>게시글 상세</title>
    <style>
        .content-container {
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        .content-header {
            border-bottom: 2px solid #ddd;
            padding-bottom: 10px;
            margin-bottom: 20px;
        }
        .content-title {
            font-size: 24px;
            margin-bottom: 10px;
        }
        .content-info {
            color: #666;
            font-size: 14px;
        }
        .content-body {
            margin: 20px 0;
            line-height: 1.6;
            padding: 20px;
            background-color: #F9F9F9;
            border-radius: 4px;
        }
        .button-group {
            margin-top: 20px;
        }
        .delete-btn {
            background-color: #F44336;
            color: white;
            padding: 10px 20px;
            border: none;
            cursor: pointer;
            border-radius: 4px;
        }
        .delete-btn:hover {
            background-color: #DA190B;
        }
        .back-btn {
            background-color: #4CAF50;
            color: white;
            padding: 10px 20px;
            border: none;
            cursor: pointer;
            text-decoration: none;
            display: inline-block;
            margin-right: 10px;
            border-radius: 4px;
        }
        .back-btn:hover {
            background-color: #45A049;
        }
    </style>
</head>
<body>
    <div class="content-container">
        <div class="content-header">
            <h1 class="content-title">{{ content.c_title }}</h1>
            <div class="content-info">
                <span>작성자: {{ content.user_id }}</span>
                <span> | </span>
                <span>작성일: {{ content.date }}</span>
            </div>
        </div>
        <div class="content-body">
            {{ content.c_text }}
        </div>
        <div class="button-group">
            <a href="/" class="back-btn">목록으로</a>
            <button class="delete-btn" onclick="deleteContent({{ content.c_id }})">삭제</button>
        </div>
    </div>
    <script>
        // 게시글 삭제를 처리하는 함수
        // contentId: 삭제할 게시글의 ID
        function deleteContent(contentId) {
            // 사용자에게 삭제 확인 메시지를 표시
            // confirm은 사용자에게 확인/취소를 보여주는 함수임.
            if (confirm('정말로 삭제하시겠습니까?')) {
                // DELETE 요청을 서버에 전송
                // `/content/${contentId}`: 삭제할 게시글의 엔드포인트
                fetch(`/content/${contentId}`, {
                    method: 'DELETE',  // HTTP DELETE 메서드 사용
                })
                // 서버 응답을 JSON 형식으로 파싱
                .then(response => {
                    // alert(response.ok)
                    if (!response.ok) {
                        throw new Error("삭제 실패: " + response.status);
                    }
                    return response.json();
                })
                // 삭제 성공 시 처리
                .then(data => {
                    alert('삭제되었습니다.');
                    window.location.href = '/';  // 메인 페이지로 리다이렉트
                })
                // 에러 시에 보여줄 것
                .catch(error => {
                    console.error('Error:', error);
                    alert('삭제 중 오류가 발생했습니다.');
                });
            }
        }
    </script>
</body>
</html>

write.html

<!DOCTYPE html>
<html>
<head>
    <title>글쓰기</title>
    <style>
        .form-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
        }
        input[type="text"], textarea {
            width: 100%;
            padding: 8px;
            margin-bottom: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        button {
            padding: 10px 20px;
            background-color: #4CAF50;
            color: white;
            border: none;
            cursor: pointer;
            border-radius: 4px;
        }
        button:hover {
            background-color: #45A049;
        }
        .container {
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>글쓰기</h1>
        <form action="/write" method="post">
            <div class="form-group">
                <label for="title">제목:</label>
                <input type="text" id="title" name="title" required>
            </div>
            <div class="form-group">
                <label for="text">내용:</label>
                <textarea id="text" name="text" rows="10" required></textarea>
            </div>
            <div class="form-group">
                <label for="user_id">작성자:</label>
                <input type="text" id="user_id" name="user_id" required>
            </div>
            <button type="submit">작성하기</button>
        </form>
    </div>
</body>
</html>



댓글 쓰기

다음 이전