from pybo import db
class Question(db.Model) :
id = db.Column(db.Integer, primary_key = True)
subject = db.Column(db.String(200), nullable = False)
content = db.Column(db.Text(), nullable = False)
create_date = db.Column(db.DateTime(), nullable = False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), nullable=False)
user = db.relationship('User',backref=db.backref('question_set'))
modify_date = db.Column(db.DateTime(), nullable=True)
class Answer(db.Model) :
id = db.Column(db.Integer, primary_key = True)
question_id = db.Column(db.Integer, db.ForeignKey('question.id', ondelete = 'cascade'))
question = db.relationship('Question', backref=db.backref('answer_set', ))
# question = db.relationship('Question', backref=db.backref('answer_set', casecade='all, delete-orphan'))
content = db.Column(db.Text(), nullable = False)
create_date = db.Column(db.DateTime(), nullable = False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), nullable=False)
user = db.relationship('User',backref=db.backref('answer_set'))
modify_date = db.Column(db.DateTime(), nullable=True)
class User(db.Model) :
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(150), unique=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
models.py 입니다.
언제 질문, 답변을 수정했는지 알 수 있는 modify_date 필드를 추가하겠습니다.
모델이 변경 했으니 migrate, upgrade 명령을 수행하겠습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% extends 'base.html' %}
{% block content %}
<div class="container my-3">
<h2 class="border-bottom py-2">{{ question.subject }}</h2>
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;">{{ question.content }}</div>
</div>
<div class="d-flex justify-content-end">
<span class="badge bg-light text-dark text-left">
<span class="mb-2"> {{ question.user.username }} </span>
<span> {{ question.create_date|datetime }} </span>
</span>
</div>
</div>
{% if g.user == question.user %}
<div class="my-3">
<a href="{{ url_for('question.modify', question_id=question.id) }}" class="btn btn-sm btn-outline-secondary">수정</a>
</div>
{% endif %}
<h5 class="border-bottom my-3">{{ question.answer_set|length }}개의 답변이 있습니다.</h5>
{% for answer in question.answer_set %}
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space : pre-line;">
{{ answer.content }}
</div>
</div>
<div class="d-flex justify-content-end">
<span class="badge bg-light text-dark text-left">
<span class="mb-2"> {{ answer.user.username }} </span>
<span> {{ answer.create_date|datetime }} </span>
</span>
</div>
</div>
{% endfor %}
<h5> {{ question.answer_set|length }}개의 답변이 있습니다. </h5>
<div>
<ul>
{% for answer in question.answer_set %}
<li> {{ answer.content }}</li>
{% endfor %}
</ul>
</div>
<form class="my-3" action="{{ url_for('answer.create', question_id=question.id) }}" method="post">
{{ form.csrf_token }}
<!-- 오류표시 Start -->
{% for field, errors in form.errors.items() %}
<div class="alert alert-danger" role="alert">
<strong>{{ form[field].label }}</strong>: {{ ','.join(errors) }}
</div>
{% endfor %}
<!-- 오류표시 End -->
<div class="form-group">
<textarea {% if not g.user %} disabled {% endif %} name="content" id="content" row="15"></textarea>
<input class="btn btn-primary" type="submit" value="답변등록">
</div>
</form>
</div>
{% endblock %}
</body>
</html>
question_detail.html 입니다.
HTMl 을 수정해서 로그인한 사용자와 글쓴이가 같은 경우에만 보여야 합니다.
{% if g.user == question.user %}
from datetime import datetime
from flask import Blueprint, render_template, request, url_for, g, flash
from .auth_views import login_required
from ..models import Question
from werkzeug.utils import redirect
from .. import db
from ..forms import QuestionForm, AnswerForm
bp = Blueprint('question', __name__, url_prefix='/question')
@bp.route('/list')
def _list():
page = request.args.get('page', type=int, default=1) # 페이지
question_list = Question.query.order_by(Question.create_date.desc())
question_list = question_list.paginate(page, per_page=10)
return render_template('question/question_list.html', question_list=question_list)
@bp.route('/detail/<int:question_id>')
def detail(question_id):
form = AnswerForm()
question = Question.query.get_or_404(question_id)
return render_template('question/question_detail.html', question=question, form=form)
@bp.route('/create/', methods=('GET', 'POST'))
@login_required
def create():
form = QuestionForm()
if request.method == 'POST' and form.validate_on_submit():
question = Question(subject=form.subject.data, content=form.content.data, create_date=datetime.now(), user=g.user)
db.session.add(question)
db.session.commit()
return redirect(url_for('main.index'))
return render_template('question/question_form.html', form=form)
@bp.route('/modify/<int:question_id>', methods=('GET','POST'))
@login_required
def modify(question_id):
question = Question.query.get_or_404(question_id)
if g.user != question.user:
flash('수정권한이 없습니다.')
return redirect(url_for('question.detail', question_id=question_id))
if request.method == 'POST':
form = QuestionForm()
if form.validate_on_submit():
form.populate_obj(question)
question.modify_date = datetime.now()
db.session.commit()
return redirect(url_for('question.detail', question_id=question_id))
else:
form = QuestionForm(obj=question)
return render_template('question/question_form.html', form=form)
question_views.py 입니다.
질문 수정은 로그인이 필요해서 @login_required을 이용해 로그인이 안 됐으면 로그인페이지로 가게 만들었고
현재 유저와 작성자와 같아야 수정할 수 있기 때문에 같지 않으면 flash('수정권한이 없습니다')
현재 페이지에 있는 question 정보를 obj에 담아 form 저장합니다.
form = QuestionForm(obj=question)
그리고 form 그에 대한 정보를 담아서 question.detail 라우트로 보냅니다.
@bp.route('/detail/<int:question_id>')
def detail(question_id):
form = AnswerForm()
question = Question.query.get_or_404(question_id)
return render_template('question/question_detail.html', question=question, form=form)
만약 같고 POST방식으로 요청되면 (수정저장 버튼 눌렀을 때) form 형식이 맞는지 확인하고
현재 페이지 정보(제목 내용 등...)을 form.popluate_obj(question) 이걸 이용해 가져옵니다.
이건 전달된 객체의 폼과 관련된 속성을 화면에 있는 폼 데이터로 채웁니다.
그 후 question.modify_date = datetime.now()로 수정일시를 저장후 commit을 이용해 저장합니다.
질문 삭제 기능을 만들겠습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% extends 'base.html' %}
{% block content %}
<div class="container my-3">
<h2 class="border-bottom py-2">{{ question.subject }}</h2>
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;">{{ question.content }}</div>
</div>
<div class="d-flex justify-content-end">
<span class="badge bg-light text-dark text-left">
<span class="mb-2"> {{ question.user.username }} </span>
<span> {{ question.create_date|datetime }} </span>
</span>
</div>
</div>
{% if g.user == question.user %}
<div class="my-3">
<a href="{{ url_for('question.modify', question_id=question.id) }}" class="btn btn-sm btn-outline-secondary">수정</a>
<a href="#" class="delete btn btn-sm btn-outline-secondary" data-uri="{{ url_for('question.delete', question_id=question.id) }}">삭제</a>
</div>
{% endif %}
<h5 class="border-bottom my-3">{{ question.answer_set|length }}개의 답변이 있습니다.</h5>
{% for answer in question.answer_set %}
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space : pre-line;">
{{ answer.content }}
</div>
</div>
<div class="d-flex justify-content-end">
<span class="badge bg-light text-dark text-left">
<span class="mb-2"> {{ answer.user.username }} </span>
<span> {{ answer.create_date|datetime }} </span>
</span>
</div>
</div>
{% endfor %}
<h5> {{ question.answer_set|length }}개의 답변이 있습니다. </h5>
<div>
<ul>
{% for answer in question.answer_set %}
<li> {{ answer.content }}</li>
{% endfor %}
</ul>
</div>
<form class="my-3" action="{{ url_for('answer.create', question_id=question.id) }}" method="post">
{{ form.csrf_token }}
<!-- 오류표시 Start -->
{% for field, errors in form.errors.items() %}
<div class="alert alert-danger" role="alert">
<strong>{{ form[field].label }}</strong>: {{ ','.join(errors) }}
</div>
{% endfor %}
<!-- 오류표시 End -->
<div class="form-group">
<textarea {% if not g.user %} disabled {% endif %} name="content" id="content" row="15"></textarea>
<input class="btn btn-primary" type="submit" value="답변등록">
</div>
</form>
</div>
{% endblock %}
{% block script %}
<script type="text/javascript">
$(document).ready(function(){
$(".delete").on("click", function(){
if(confirm("정말로 삭제하시겠습니까?")) {
location.href = $(this).data('uri');
}
});
});
</script>
{% endblock %}
</body>
</html>
question_detail.html 입니다.
href 속성을 #으로 하고 data-uri 라는 기능을 넣었습니다.
jQuery에서 $(this).data('uri')로 삭제 실행하는 URL을 얻으려고 했습니다.
{% block script %}
{% endblock %} 을 이용했는데 jQuery를 템플릿에서 사용할 수 있도록 약간 수정해야하기 때문입니다.
자바스크립트문을 설명하자면 화면이 준비 됐을 때 $(document).ready(function() .... function 내용이 수행됩니다.
delete 클래스가 클릭 되면 function 이하에 내용이 수행됩니다.
confirm은 확인창이 어떤 형식으로 나올지에 대한 것입니다.
만약 예를 누를 경우
location.href = $(this).data('uri') 가 실행 되는 것이죠 이것의 의미는
현재 누른 것의 data-uri를 href로 연다는 것입니다.
여기선 question.delete로 되어있습니다. 그에 대한 내용을 추가하겠습니다. (라우트)
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='bootstrap.min.css') }}">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Title</title>
</head>
<body>
{% include "navbar.html" %}
<!-- 기본 템플릿에 삽입할 내용 Start -->
{% block content %}
{% endblock %}
<!-- 기본 템플릿에 삽입할 내용 End -->
<!-- jQuery JS -->
<script src="{{ url_for('static', filename='jquery-3.6.0.min.js') }}"> </script>
<!-- Bootstarp JS -->
<script src="{{ url_for('static', filename='bootstrap.min.js') }}"> </script>
<!-- 자바스크립트 -->
{% block script %}
{% endblock %}
</body>
</html>
base.html 입니다.
{% block script %}
{% endblock %} 을 추가해서 제이쿼리를 이용할 수 있게 만들었습니다.
from datetime import datetime
from flask import Blueprint, render_template, request, url_for, g, flash
from .auth_views import login_required
from ..models import Question
from werkzeug.utils import redirect
from .. import db
from ..forms import QuestionForm, AnswerForm
bp = Blueprint('question', __name__, url_prefix='/question')
@bp.route('/list')
def _list():
page = request.args.get('page', type=int, default=1) # 페이지
question_list = Question.query.order_by(Question.create_date.desc())
question_list = question_list.paginate(page, per_page=10)
return render_template('question/question_list.html', question_list=question_list)
@bp.route('/detail/<int:question_id>')
def detail(question_id):
form = AnswerForm()
question = Question.query.get_or_404(question_id)
return render_template('question/question_detail.html', question=question, form=form)
@bp.route('/create/', methods=('GET', 'POST'))
@login_required
def create():
form = QuestionForm()
if request.method == 'POST' and form.validate_on_submit():
question = Question(subject=form.subject.data, content=form.content.data, create_date=datetime.now(), user=g.user)
db.session.add(question)
db.session.commit()
return redirect(url_for('main.index'))
return render_template('question/question_form.html', form=form)
@bp.route('/modify/<int:question_id>', methods=('GET','POST'))
@login_required
def modify(question_id):
question = Question.query.get_or_404(question_id)
if g.user != question.user:
flash('수정권한이 없습니다.')
return redirect(url_for('question.detail', question_id=question_id))
if request.method == 'POST':
form = QuestionForm()
if form.validate_on_submit():
form.populate_obj(question)
question.modify_date = datetime.now()
db.session.commit()
return redirect(url_for('question.detail', question_id=question_id))
else:
form = QuestionForm(obj=question)
return render_template('question/question_form.html', form=form)
@bp.route('delete/,<int:question_id>')
@login_required
def delete(question_id):
question = Question.query.get_or_404(question_id)
if g.user != question.user:
flash('삭제권한이 없습니다.')
return redirect(url_for('question.detail', question_id=question_id))
db.session.delete(question)
db.session.commit()
return redirect(url_for('question._list'))
question_views.py 입니다.
question.delete를 구현한 것입니다.
로그인이 되어야하고 로그인한 아이디와 작성자와 같지 않으면 삭제 권한이 없는 것이고
같다면 db에서 question 객체를 삭제후 적용시키고 list화면으로 나옵니다.
답변을 수정하고 삭제하는 버튼을 추가하겠습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% extends 'base.html' %}
{% block content %}
<div class="container my-3">
<h2 class="border-bottom py-2">{{ question.subject }}</h2>
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;">{{ question.content }}</div>
</div>
<div class="d-flex justify-content-end">
<span class="badge bg-light text-dark text-left">
<span class="mb-2"> {{ question.user.username }} </span>
<span> {{ question.create_date|datetime }} </span>
</span>
</div>
</div>
{% if g.user == question.user %}
<div class="my-3">
<a href="{{ url_for('question.modify', question_id=question.id) }}" class="btn btn-sm btn-outline-secondary">수정</a>
<a href="#" class="delete btn btn-sm btn-outline-secondary" data-uri="{{ url_for('question.delete', question_id=question.id) }}">삭제</a>
</div>
{% endif %}
<h5 class="border-bottom my-3">{{ question.answer_set|length }}개의 답변이 있습니다.</h5>
{% for answer in question.answer_set %}
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space : pre-line;">
{{ answer.content }}
</div>
</div>
<div class="d-flex justify-content-end">
<span class="badge bg-light text-dark text-left">
<span class="mb-2"> {{ answer.user.username }} </span>
<span> {{ answer.create_date|datetime }} </span>
</span>
</div>
</div>
{% if g.user == answer.user %}
<div class="my-3">
<a href="{{ url_for('answer.modify', answer_id=answer.id) }}" class="btn btn-sm btn-outline-secondary">수정</a>
<a href="#" class="delete btn btn-sm btn-outline-secondary" data-uri="{{ url_for('answer.delete', answer_id=answer.id) }}">삭제</a>
</div>
{% endif %}
{% endfor %}
<h5> {{ question.answer_set|length }}개의 답변이 있습니다. </h5>
<div>
<ul>
{% for answer in question.answer_set %}
<li> {{ answer.content }}</li>
{% endfor %}
</ul>
</div>
<form class="my-3" action="{{ url_for('answer.create', question_id=question.id) }}" method="post">
{{ form.csrf_token }}
<!-- 오류표시 Start -->
{% for field, errors in form.errors.items() %}
<div class="alert alert-danger" role="alert">
<strong>{{ form[field].label }}</strong>: {{ ','.join(errors) }}
</div>
{% endfor %}
<!-- 오류표시 End -->
<div class="form-group">
<textarea {% if not g.user %} disabled {% endif %} name="content" id="content" row="15"></textarea>
<input class="btn btn-primary" type="submit" value="답변등록">
</div>
</form>
</div>
{% endblock %}
{% block script %}
<script type="text/javascript">
$(document).ready(function(){
$(".delete").on("click", function(){
if(confirm("정말로 삭제하시겠습니까?")) {
location.href = $(this).data('uri');
}
});
});
</script>
{% endblock %}
</body>
</html>
question_detail.html 입니다.
댓글 수정 삭제 버튼을 만들었습니다.
from datetime import datetime
from flask import Blueprint, url_for, request, render_template, g, flash
from werkzeug.utils import redirect
from .auth_views import login_required
from .. import db
from ..models import Question, Answer
from ..forms import AnswerForm
bp = Blueprint('answer', __name__, url_prefix='/answer')
@bp.route('/create/<int:question_id>', methods=('POST', ))
@login_required
def create(question_id):
form = AnswerForm()
question = Question.query.get_or_404(question_id) # 그 질문에 해당하는 번호를 가져온다.
if form.validate_on_submit():
content = request.form['content'] # name 속성이 content인 값
answer = Answer(content=content, create_date=datetime.now(), user=g.user) # textArea 내용, 작성시각, 작성자
question.answer_set.append(answer) # question.answer_set 은 Answer과 동일 즉, 거기에다가 값을 추가하는 것임
db.session.commit() # db에 저장
return redirect(url_for('question.detail', question_id=question_id)) # 상세 질문과 답변이 있는 페이지로 전달
return render_template('question/question_detail.html', question=question, form=form)
@bp.route('/modify/<int:answer_id>', methods=('GET','POST'))
@login_required
def modify(answer_id):
answer = Answer.query.get_or_404(answer_id)
if g.user != answer.user:
flash('수정권한이 없습니다.')
return redirect(url_for('question.detail', question_id=answer.question.id))
if request.method == "POST":
form = AnswerForm()
if form.validate_on_submit():
form.populate_obj(answer)
answer.modify_date = datetime.now()
db.session.commit()
return redirect(url_for('question.detail', question_id=answer.question.id))
else:
form = AnswerForm(obj=answer)
return render_template('answer/answer_form.html', answer=answer, form=form)
@bp.route('delete/,<int:answer_id>')
@login_required
def delete(answer_id):
answer = Answer.query.get_or_404(answer_id)
question_id = answer.question.id
if g.user != answer.user:
flash('삭제권한이 없습니다.')
db.session.delete(answer)
db.session.commit()
return redirect(url_for('question.detail', question_id=question_id))
answer_views.py 입니다
위에 내용하고 거의 동일하기 때문에 설명은 넘어가겠습니다. (수정 삭제 기능을 넣었습니다.)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% extends 'base.html' %}
{% block content %}
<div class="container my-3">
<form method="post" class="post-form">
{{ form.csrf_token }}
{% include "form_errors.html" %}
<div class="form-group">
<label for="content">답변내용</label>
<textarea class="form-control" name="content" id="content" rows="10">
{{ form.content.data or '' }}
</textarea>
</div>
<button type="sumbmit" class="btn btn-primary">저장하기</button>
</form>
</div>
{% endblock %}
</body>
</html>
templates/answer/answer_form.html 입니다.
댓글 수정화면을 새로 만들었습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% extends 'base.html' %}
{% block content %}
<div class="container my-3">
<h2 class="border-bottom py-2">{{ question.subject }}</h2>
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;">{{ question.content }}</div>
</div>
<div class="d-flex justify-content-end">
{% if question.modify_date %}
<span class="badge bg-light text-dark p-2 text-start mx-3">
<div class="mb-2">modified at</div>
<div> {{ question.modify_date|datetime }}</div>
</span>
{% endif %}
<span class="badge bg-light text-dark text-start">
<div class="mb-2"> {{ question.user.username }} </div>
<div> {{ question.create_date|datetime }} </div>
</span>
</div>
</div>
{% if g.user == question.user %}
<div class="my-3">
<a href="{{ url_for('question.modify', question_id=question.id) }}" class="btn btn-sm btn-outline-secondary">수정</a>
<a href="#" class="delete btn btn-sm btn-outline-secondary" data-uri="{{ url_for('question.delete', question_id=question.id) }}">삭제</a>
</div>
{% endif %}
<h5 class="border-bottom my-3">{{ question.answer_set|length }}개의 답변이 있습니다.</h5>
{% for answer in question.answer_set %}
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space : pre-line;">
{{ answer.content }}
</div>
</div>
<div class="d-flex justify-content-end">
{% if answer.modify_date %}
<span class="badge bg-light text-dark text-start p-2 mx-3">
<div class="mb-2"> modified at </div>
<div> {{ answer.modify_date|datetime }} </div>
</span>
{% endif %}
<span class="badge bg-light text-dark text-start">
<div class="mb-2"> {{ answer.user.username }} </div>
<div> {{ answer.create_date|datetime }} </div>
</span>
</div>
</div>
{% if g.user == answer.user %}
<div class="my-3">
<a href="{{ url_for('answer.modify', answer_id=answer.id) }}" class="btn btn-sm btn-outline-secondary">수정</a>
<a href="#" class="delete btn btn-sm btn-outline-secondary" data-uri="{{ url_for('answer.delete', answer_id=answer.id) }}">삭제</a>
</div>
{% endif %}
{% endfor %}
<h5> {{ question.answer_set|length }}개의 답변이 있습니다. </h5>
<div>
<ul>
{% for answer in question.answer_set %}
<li> {{ answer.content }}</li>
{% endfor %}
</ul>
</div>
<form class="my-3" action="{{ url_for('answer.create', question_id=question.id) }}" method="post">
{{ form.csrf_token }}
<!-- 오류표시 Start -->
{% for field, errors in form.errors.items() %}
<div class="alert alert-danger" role="alert">
<strong>{{ form[field].label }}</strong>: {{ ','.join(errors) }}
</div>
{% endfor %}
<!-- 오류표시 End -->
<div class="form-group">
<textarea {% if not g.user %} disabled {% endif %} name="content" id="content" row="15"></textarea>
<input class="btn btn-primary" type="submit" value="답변등록">
</div>
</form>
</div>
{% endblock %}
{% block script %}
<script type="text/javascript">
$(document).ready(function(){
$(".delete").on("click", function(){
if(confirm("정말로 삭제하시겠습니까?")) {
location.href = $(this).data('uri');
}
});
});
</script>
{% endblock %}
</body>
</html>
quesiton_detail.html 입니다.
수정해서 글이나 댓글 수정시각을 추가시켰습니다.
'플라스크 (추후 수정)' 카테고리의 다른 글
Flask 글, 댓글 작성자 표시해보기 DB 테이블 수정과 DB접근 (0) | 2021.08.20 |
---|---|
Flask 로그인, 로그아웃 check_pass_word_hash, g, session, @bp.before_app_request (0) | 2021.08.16 |
Flask 회원가입기능 추가 generate_password_hash, query.filter_by (0) | 2021.08.15 |
Flask 질문에 달린 답변 개수 표시하기 (0) | 2021.08.14 |
Flask 게시물 번호 바꾸기 (0) | 2021.08.14 |