댓글 기능 추가

책 '점프 투 플라스크'를 공부하면서 정리한 내용입니다.
출처 : https://wikidocs.net/81060

댓글에 사용할 모델 생성

Comment 모델 생성

class Comment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), nullable=False)
    user = db.relationship('User', backref=db.backref('comment_set'))
    content = db.Column(db.Text(), nullable=False)
    create_date = db.Column(db.DateTime(), nullable=False)
    modify_date = db.Column(db.DateTime())
    question_id = db.Column(db.Integer, db.ForeignKey('question.id', ondelete='CASCADE'), nullable=True)
    question = db.relationship('Question', backref=db.backref('comment_set'))
    answer_id = db.Column(db.Integer, db.ForeignKey('answer.id', ondelete='CASCADE'), nullable=True)
    answer = db.relationship('Answer', backref=db.backref('comment_set'))

질문 or 답변을 DB에서 삭제하면 연관된 댓글도 삭제될 수 있도록 ondelete='CASCADE' 옵션 설정

question_id 필드, answer_id 필드 중 하나에만 값이 저장되므로 두 필드는 모두 nullable=True여야 한다.

모델을 수정했으니 migrate, upgrade 명령 수행

질문 댓글 기능 추가

1. 댓글 목록, 댓글 입력 링크 추가

question_detail.html

<!-- 질문 댓글 Start -->
            {% if question.comment_set|length > 0 %}
            <div class="mt-3">
            {% for comment in question.comment_set %}
                <div class="comment py-2 text-muted">
                    <span style="white-space: pre-line;">{{ comment.content }}</span>
                    <span>
                        - {{ comment.user.username }}, {{ comment.create_date|datetime }}
                        {% if comment.modify_date %}
                        (수정:{{ comment.modify_date|datetime }})
                        {% endif %}
                    </span>
                    {% if g.user == comment.user %}
                    <a href="{{ url_for('comment.modify_question', comment_id=comment.id) }}" class="small">수정</a>,
                    <a href="#" class="small delete"
                       data-uri="{{ url_for('comment.delete_question', comment_id=comment.id) }}">삭제</a>
                    {% endif %}
                </div>
            {% endfor %}
            </div>
            {% endif %}
            <div>
                <a href="{{ url_for('comment.create_question', question_id=question.id) }}"
                   class="small"><small>댓글 추가 ..</small></a>
            </div>
            <!-- 질문 댓글 End -->

2. comment 클래스 CSS

comment 클래스는 댓글을 작게 보여 주는 클래스로 CSS를 별도 작성해야 함

댓글마다 상단에 점선 추가, 글꼴 크기 0.7em 설정

.comment {
    border-top:dotted 1px #ddd;
    font-size:0.7em;
}

3. 질문 댓글 폼 작성

forms.py 파일에 댓글 등록 시 사용할 CommentForm 클래스 추가

class CommentForm(FlaskForm):
    content = TextAreaField('내용', validators=[DataRequired()])

4. 질문 댓글 등록 함수 생성

comment_views.py 생성

질문 상세 템플릿의 댓글추가 링크에 해당하는 create_question 함수 작성

from datetime import datetime

from flask import Blueprint, url_for, request, render_template, g
from werkzeug.utils import redirect

from pybo import db
from pybo.forms import CommentForm
from pybo.models import Question, Comment
from pybo.views.auth_views import login_required

bp = Blueprint('comment', __name__, url_prefix='/comment')


@bp.route('/create/question/<int:question_id>', methods=('GET', 'POST'))
@login_required
def create_question(question_id):
    form = CommentForm()
    question = Question.query.get_or_404(question_id)
    if request.method == 'POST' and form.validate_on_submit():
        comment = Comment(user=g.user, content=form.content.data, create_date=datetime.now(), question=question)
        db.session.add(comment)
        db.session.commit()
        return redirect(url_for('question.detail', question_id=question_id))
    return render_template('comment/comment_form.html', form=form)

질문에 달린 댓글이니까 Comment 모델 객체를 생성할 때 question 필드에 값을 설정했다.

5. 블루프린트 추가

comment_views.py 파일에 블루프린트를 추가했으니까 __init__.py 파일에도 블루프린트를 등록해줘야 한다.

from .views import main_views, question_views, answer_views, auth_views, comment_views

app.register_blueprint(comment_views.bp)

6. 질문 댓글 템플릿 작성

comment_form.html 템플릿 작성

{% extends 'base.html' %}
{% block content %}
<div class="container my-3">
    <h5 class="border-bottom pb-2">댓글등록하기</h5>
    <form method="post" class="post-form my-3">
        {{ form.csrf_token }}
        {% include "form_errors.html" %}
        <div class="form-group">
            <label for="content">댓글내용</label>
            <textarea class="form-control" name="content" id="content"
                      rows="3">{{ form.content.data or '' }}</textarea>
        </div>
        <button type="submit" class="btn btn-primary">저장하기</button>
    </form>
</div>
{% endblock %}

7. 질문 댓글 수정 함수

댓글 수정을 위해 comment_views.py에서 modify_question 함수 작성

from flask import Blueprint, url_for, request, render_template, g, flash

@bp.route('/modify/question/<int:comment_id>', methods=('GET', 'POST'))
@login_required
def modify_question(comment_id):
    comment = Comment.query.get_or_404(comment_id)
    if g.user != comment.user:
        flash('수정권한이 없습니다')
        return redirect(url_for('question.detail', question_id=comment.question.id))
    if request.method == 'POST':
        form = CommentForm()
        if form.validate_on_submit():
            form.populate_obj(comment)
            comment.modify_date = datetime.now()  # 수정일시 저장
            db.session.commit()
            return redirect(url_for('question.detail', question_id=comment.question.id))
    else:
        form = CommentForm(obj=comment)
    return render_template('comment/comment_form.html', form=form)

8. 질문 댓글 삭제 함수

comment_views.py에 delete_question 함수 작성

@bp.route('/delete/question/<int:comment_id>')
@login_required
def delete_question(comment_id):
    comment = Comment.query.get_or_404(comment_id)
    question_id = comment.question.id
    if g.user != comment.user:
        flash('삭제권한이 없습니다')
        return redirect(url_for('question.detail', question_id=question_id))
    db.session.delete(comment)
    db.session.commit()
    return redirect(url_for('question.detail', question_id=question_id))

답변 댓글 기능

1. 답변 댓글 목록과 댓글 입력 링크 추가

question_detail.html

 <!-- 답변 댓글 Start -->
            {% if answer.comment_set|length > 0 %}
            <div class="mt-3">
            {% for comment in answer.comment_set %}
                <div class="comment py-2 text-muted">
                    <span style="white-space: pre-line;">{{ comment.content }}</span>
                    <span>
                        - {{ comment.user.username }}, {{ comment.create_date|datetime }}
                        {% if comment.modify_date %}
                        (수정:{{ comment.modify_date|datetime }})
                        {% endif %}
                    </span>
                    {% if g.user == comment.user %}
                    <a href="{{ url_for('comment.modify_answer', comment_id=comment.id) }}" class="small">수정</a>,
                    <a href="#" class="small delete"
                       data-uri="{{ url_for('comment.delete_answer', comment_id=comment.id) }}">삭제</a>
                    {% endif %}
                </div>
            {% endfor %}
            </div>
            {% endif %}
            <div>
                <a href="{{ url_for('comment.create_answer', answer_id=answer.id) }}"
                   class="small"><small>댓글 추가 ..</small></a>
            </div>
            <!-- 답변 댓글 End -->

2. 답변 댓글 등록, 수정, 삭제 함수

comment_views.py

from pybo.models import Question, Comment, Answer

@bp.route('/create/answer/<int:answer_id>', methods=('GET', 'POST'))
@login_required
def create_answer(answer_id):
    form = CommentForm()
    answer = Answer.query.get_or_404(answer_id)
    if request.method == 'POST' and form.validate_on_submit():
        comment = Comment(user=g.user, content=form.content.data, create_date=datetime.now(), answer=answer)
        db.session.add(comment)
        db.session.commit()
        return redirect(url_for('question.detail', question_id=answer.question.id))
    return render_template('comment/comment_form.html', form=form)

@bp.route('/modify/answer/<int:comment_id>', methods=('GET', 'POST'))
@login_required
def modify_answer(comment_id):
    comment = Comment.query.get_or_404(comment_id)
    if g.user != comment.user:
        flash('수정권한이 없습니다')
        return redirect(url_for('question.detail', question_id=comment.answer.id))
    if request.method == 'POST':
        form = CommentForm()
        if form.validate_on_submit():
            form.populate_obj(comment)
            comment.modify_date = datetime.now()  # 수정일시 저장
            db.session.commit()
            return redirect(url_for('question.detail', question_id=comment.answer.question.id))
    else:
        form = CommentForm(obj=comment)
    return render_template('comment/comment_form.html', form=form)


@bp.route('/delete/answer/<int:comment_id>')
@login_required
def delete_answer(comment_id):
    comment = Comment.query.get_or_404(comment_id)
    question_id = comment.answer.question.id
    if g.user != comment.user:
        flash('삭제권한이 없습니다')
        return redirect(url_for('question.detail', question_id=question_id))
    db.session.delete(comment)
    db.session.commit()
    return redirect(url_for('question.detail', question_id=question_id))

좋은 웹페이지 즐겨찾기