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>
Tags:
클라우드