모든 항목, 해야할 항목, 완료된 항목 세 가지로 분류하여 메모장을 만들었습니다. 삭제와 업데이트 기능을 구현하였고 MongoDB와 연동하여 데이터가 삭제 및 업데이트가 가능합니다.
app.py
#Call Lib
from flask import Flask
from flask import render_template
from flask import request
from flask import redirect, url_for
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired # 데이터 유효성 검사
from pymongo import MongoClient
from bson import ObjectId # MongoDB 데이터 출력할 때 필요
from datetime import datetime
class TextForm(FlaskForm): # 텍스트 폼 생성
content = StringField('내용', validators=[DataRequired()]) # 메모가 존재하면 content에 저장
#Config setting
app = Flask(__name__)
app.config['SECRET_KEY'] = "secret"
connection = MongoClient('localhost', 27017) #ip, port 데이터베이스 연결
db = connection.project
todos = db.todo # todo 컬렉션 생성
#Error Process
@app.errorhandler(404)
def page_not_found(error):
return render_template('page_not.html'), 404
#home
@app.route("/")
@app.route("/about")
def home_page():
return render_template('about.html')
#All list page
@app.route("/all") # 전체 항목
def all_page():
stat = "All list"
todolist = todos.find().sort('date',-1) # todos 컬렉션에서 모든 데이터 내림차순으로 가져옴
form = TextForm()
return render_template('index.html', todos=todolist, stat=stat, form=form)
#Active item list
@app.route("/active") # 해야 할 항목
def active_page():
stat = "Active list"
todolist = todos.find({"done":"no"}).sort('date',-1) # 수행되지 않은 항목 추출
form = TextForm()
return render_template('index.html', todos=todolist, stat=stat, form=form)
#Completed item list
@app.route("/completed") # 완료된 항목
def complete_page():
stat = "Completed list"
todolist = todos.find({"done":"yes"}).sort('date',-1)
form = TextForm()
return render_template('index.html', todos=todolist, stat=stat, form=form)
#Update memo
@app.route("/update")
def update_page():
id=request.values.get("_id")
task=todos.find({"_id":ObjectId(id)})[0]
form = TextForm()
return render_template('update.html', task=task, form=form)
#New memo
@app.route("/action", methods=['GET','POST'])
def action_add():
form = TextForm()
if form.validate_on_submit():
contents = request.form['content'] # index.html에서 넘겨준 content 저장
date = datetime.today()
primary = request.values.get('primary') # 중요도 저장
todos.insert_one({"contents":contents, "date":date, "primary":primary, "done":"no"}) # 데이터베이스에 삽입
return """<script>
window.location = document.referrer; # 이전 페이지로 돌아감
</script>"""
else:
return render_template('page_not.html')
#Done memo change
@app.route("/done")
def done_add():
id=request.values.get("_id")
task=todos.find({"_id":ObjectId(id)})
if(task[0]["done"]=="yes"): # 체크 해제 할 때
todos.update_one({"_id":ObjectId(id)}, {"$set": {"done":"no"}})
else: # 체크 할 때
todos.update_one({"_id":ObjectId(id)}, {"$set": {"done":"yes"}})
return """<script>
window.location = document.referrer;
</script>"""
#Delete memo
@app.route("/delete")
def action_delete():
key=request.values.get("_id")
todos.delete_one({"_id":ObjectId(key)}) # 삭제
return """<script>
window.location = document.referrer;
</script>"""
#Done memo update
@app.route("/action2", methods=['GET','POST'])
def done_update():
if request.method == 'POST':
key = request.values.get("_id")
contents = request.form['content']
primary = request.values.get('primary')
todos.update_one({"_id":ObjectId(key)}, {'$set':{"contents":contents, "primary":primary}}) # 수정
return redirect(url_for('all_page'))
else:
return render_template("page_not.html")
if __name__ == "__main__":
app.run()
base.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Server</title>
<link rel="icon" href="/static/images/favicon.png">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<!-- jQuery library -->
<!-- Popper JS -->
<!-- Latest compiled JavaScript -->
<!-- fontawesome -->
<link rel="stylesheet" type="text/css"
</head>
<body>
<div class="text-left mx-3">
<h2>
<i class="fa fa-sticky-note"></i>
<strong>할 일을 메모하자!</strong>
</h2>
</div>
{% include "nav.html" %} <!-- nav.html 삽입 -->
{% block content %}{% endblock %} <!-- 카드 컴포넌트 -->
{% include "footer.html" %} <!-- footer.html 삽입 -->
</body>
<style>
html, body {
height: 100%;
}
</style>
</html>
nav.html
<nav class="navbar navbar-expand-md navbar-dark bg-dark"> <!-- 중간 화면 이상이면 가로로 나열 아니면 강조된 세 메뉴를 버튼을 통해 세로로 나열 -->
<a class="navbar-brand mb-0 h1" href="/">About</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#myNavbar">
<span class="navbar-toggler-icon"></span> <!-- 햄버거 모양 아이콘 -->
</button>
<div class="navbar-collapse collapse" id="myNavbar">
<ul class="nav navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="/all">모든 항목</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="/active">해야 할 항목</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="/completed">완료된 항목</a>
</li>
</ul>
</div>
</nav>
about.html
{% extends "base.html" %}
{% block content %}
<body class="text-center">
<div class="card shadow-sm mx-2 mt-2">
<div class="card-header font-weight-bold">
About
</div>
<div class="card-body">
<div class="card-text">
파이썬 플라스크 웹 프레임워크를 사용해 만들어진 웹 페이지입니다.<br>
<img class="img-fluid" src="{{ url_for('static', filename='images/flask.png') }}"> <!-- 반응형 이미지 -->
</div>
</div>
</div>
</body>
{% endblock %}
index.html
{% extends "base.html" %}
{% block content %}
<div style="height: 80%;">
<div class="card shadow-lg mx-2 mt-2"> <!-- 좌우, top 마진 설정 -->
<div class="card-header font-weight-bold text-center">
Input memo
</div>
<div class="card-body">
<form action="/action" method="POST" class="mx-2"> <!-- 데이터 입력하면 /action으로 이동 -->
{{ form.csrf_token }}
<div class="input mb-2">
{{ form.content(class="form-control", placeholder="메모를 입력하세요") }}
</div>
<div class="input-group">
<select name="primary" class="input-group-append form-control">
<option selected disabled>중요도 선택</option>
<option>Low</option>
<option>Medium</option>
<option>High</option>
</select>
<div class="input-group-append">
<button type="submit" class="btn btn-outline-secondary float-right">
추가 <i class="fa fa-plus-circle fa-lg"></i>
</button>
</div>
</div>
</form>
</div>
</div>
<div class="card shadow-sm mx-2 mt-2">
<div class="card-header font-weight-bold text-center">
{{ stat }} <!-- 항목 종류 출력 -->
</div>
<div class="card-body">
{% include "list.html" %}
</div>
</div>
</div>
{% endblock %}
list.html
<table class="table table-sm table-striped">
<thead class="text-center">
<tr>
<th>상태</th>
<th class="text-left">내용</th>
<th></th>
<th>삭제</th>
<th>변경</th>
</tr>
</thead>
<tbody>
{% for todo in todos %}
<tr>
<td class="text-center"><a href="./done?_id={{ todo['_id'] }}"><input type="image" <!-- done page로 id값 전달
src="static/images/{{todo['done']}}.png" width="30" alt="Submit ME"></a></td> <!-- 상태에 따른 이미지 출력 -->
<td>
<strong>{{ todo["contents"] }}</strong><br> <!-- 메모 출력 -->
- {{ todo["date"].strftime("%Y-%m-%d") }}
</td>
{% if todo["primary"] == "Low" %}
<td class="badge badge-pill badge-success align-middle">{{ todo["primary"] }}</td>
{% elif todo["primary"] == "Medium" %}
<td class="badge badge-pill badge-warning align-middle">{{ todo["primary"] }}</td>
{% elif todo["primary"] == "High" %}
<td class="badge badge-pill badge-danger align-middle">{{ todo["primary"] }}</td>
{% else %}
<td class="badge badge-pill badge-light align-middle">{{ todo["primary"] }}</td>
{% endif %}
<td class="text-center">
<img src="{{ url_for('static',filename='images/trash.png') }}" , width="30" data-toggle="modal"
data-target="#exampleModal" ) /></td> <!-- 삭제 문구 출력 -->
<td class="text-center"><a href="./update?_id={{ todo['_id'] }}"> <!-- 수정 -->
<img src="{{ url_for('static',filename='images/edit.png') }}" , width="30" ) /></a></td>
</tr>
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">삭제 경고</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span> <!-- x 아이콘 -->
</button>
</div>
<div class="modal-body">
정말로 삭제하시겠습니까?
</div>
<div class="modal-footer">
<a href="./delete?_id={{ todo['_id'] }}"><button type="button" <!-- 삭제 -->
class="btn btn-danger">삭제</button></a>
<button type="button" class="btn btn-secondary" data-dismiss="modal">취소</button>
</div>
</div>
</div>
</div>
{% endfor %}
</tbody>
</table>
update.html
{% extends "base.html" %}
{% block content %}
<body class="text-center">
<div class="card shadow-sm mx-2 mt-2">
<div class="card-header font-weight-bold">
Update memo
</div>
<div class="card-body">
<form action="/action2" method="POST" class="mx-2">
{{ form.csrf_token }}
<input type="hidden" name="_id" value="{{ task['_id'] }}">
<div class="input mb-2">
{{ form.content(class="form-control", placeholder=task['contents'], value=task['contents']) }} <!-- 수정할 메모 출력 -->
</div>
<div class="input-group">
<select name="primary" class="input-group-append form-control">
<option value="{{ task['primary'] }}" selected>{{ task['primary'] }}</option>
<option>None</option>
<option>Low</option>
<option>Medium</option>
<option>High</option>
</select>
<div class="input-group-append">
<button type="submit" class="btn btn-outline-secondary float-right">
변경 <i class="fa fa-exchange fa-lg"></i>
</button>
</div>
</div>
</form>
</div>
</div>
</body>
{% endblock %}
footer.html
<footer class="fixed-bottom bg-dark text-light align-items-center px-3">
<div class="container">
<div class="d-flex justify-content-end">
<div>
<i class="fa fa-copyright"></i> dsz08082@naver.com
</div>
</div>
</div>
</footer>
page_not.html
{% extends "base.html" %}
{% block content %}
<body>
<div class="bg-danger text-center" style="height: 87%;">
<h3>잘못 접근하셨어요 :(</h3><br>
<h1>페이지를 다른데 두고 오셨네요</h1>
</div>
</body>
{% endblock %}
'웹 개발 > Back End' 카테고리의 다른 글
스프링 입문 - 템플릿 엔진 개념 (0) | 2021.12.31 |
---|---|
파이썬 Flask와 PostgreSQL를 이용한 쇼핑몰 구현 프로젝트 (0) | 2021.12.15 |
[Python Flask] - 파일 서버 만들기 (0) | 2021.11.13 |
파이썬 플라스크(Flask)의 Jinja2(신사2)를 이용한 구구단 구현 (0) | 2021.11.12 |
웹 프레임워크 및 IDE(통합개발환경) 종류 (0) | 2021.11.09 |
댓글