개발
점프 투 장고 추가 기능(댓글)
wafla
2024. 11. 7. 00:52
댓글 기능 추가
이번엔 댓글 기능을 구현해보려합니다.
질문에 댓글 기능은 필요 없을 것 같아서 답변에만 댓글 기능을 추가해 보겠습니다.
먼저 models.py에서 Comment 모델을 생성해줍니다.
class Comment(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='author_comment')
answer = models.ForeignKey(Answer, on_delete=models.CASCADE)
content = models.TextField()
create_date = models.DateTimeField()
modify_date = models.DateTimeField(null=True, blank=True)
사용자, 답변, 내용, 생성 시간 정도면 충분합니다. 모델을 추가했으면 makemigrations와 migrate 명령어를 통해 실제 데이터베이스를 생성해 주는 것도 잊으면 안 됩니다.
forms.py도 작성해줍니다.
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content']
labels = {
'content' : '댓글내용',
}
답변 추천 버튼 옆에 댓글을 확인할 수 있는 버튼도 추가해줬습니다.
question_detail.html
<div class="my-3">
<a href="javascript:void(0)" data-uri="{% url 'pybo:answer_vote' answer.id %}"
class="recommend btn btn-sm btn-outline-secondary">추천
<span class="badge rounded-pill bg-success">{{answer.voter.count}}</span>
</a>
<a href="javascript:void(0)" class="btn btn-sm btn-outline-secondary show-comments" data-answer-id="{{ answer.id }}">
댓글 <span class="badge rounded-pill bg-secondary">{{ answer.comment_set.count }}</span>
</a>
{% if request.user == answer.author %}
<a href="{% url 'pybo:answer_modify' answer.id %}"
class="btn btn-sm btn-outline-secondary">수정</a>
<a href="#" class="delete btn btn-sm btn-outline-secondary"
data-uri="{% url 'pybo:answer_delete' answer.id %}">삭제</a>
{% endif %}
</div>
바로 밑에 댓글과 댓글 작성 칸을 보여주는 코드도 작성했습니다.
<!-- 댓글 표시 & 댓글 작성 영역 -->
<div id="comments_{{ answer.id }}" class="comment-section mt-3" style="display: none;">
<!-- 댓글 표시 -->
{% for comment in answer.comment_set.all %}
<div class="card my-1">
<div class="card-body">
<p class="card-text">{{ comment.content|linebreaks }}</p>
<small class="text-muted">{{ comment.author.username }} - {{ comment.create_date }} </small>
{% if request.user == comment.author %}
<a href="#" class="delete btn btn-sm btn-outline-secondary"
data-uri="{% url 'pybo:comment_delete' comment.id sort %}">삭제</a>
{% endif %}
</div>
</div>
{% empty %}
<p class="text-muted">아직 댓글이 없습니다.</p>
{% endfor %}
<!-- 댓글 작성 -->
<form action="{% url 'pybo:comment_create' answer.id sort %}" method="post" class="mt-2">
{% csrf_token %}
<div class="mb-2">
<textarea name="content" class="form-control" rows="2" placeholder="댓글을 입력하세요."></textarea>
</div>
<button type="submit" class="btn btn-sm btn-primary">댓글 작성</button>
</form>
</div>
위의 코드는 버튼이 눌렸을 때만 화면에 나타나야합니다. 따라서 display: none;으로 해두고 javascript를 통해 display 값을 변경합시다. 또 댓글을 작성하고 삭제한 뒤 해당 댓글 창이 계속 열려있을 수 있는 코드도 추가했습니다.
<script type='text/javascript'>
// 댓글 표시 & 댓글 작성 버튼 클릭
const commentButtons = document.querySelectorAll('.show-comments');
commentButtons.forEach(button => {
button.addEventListener('click', function() {
const answerId = this.getAttribute('data-answer-id');
const commentsSection = document.getElementById(`comments_${answerId}`);
if (commentsSection) {
commentsSection.style.display = commentsSection.style.display === 'none' ? 'block' : 'none';
}
});
});
// 댓글 작성/삭제 후 해당 답변 댓글 창 열기
document.addEventListener("DOMContentLoaded", function() {
const hash = window.location.hash;
if (hash && hash.startsWith("#answer_")) {
const answerId = hash.replace("#answer_", ""); // 해시에서 답변 ID 추출
const commentsSection = document.getElementById(`comments_${answerId}`);
if (commentsSection) {
commentsSection.style.display = "block";
}
}
});
</script>
삭제 버튼은
{% url 'pybo:comment_delete' comment.id sort %}
댓글 작성은
{% url 'pybo:comment_create' answer.id sort %}
이렇게 경로를 설정했습니다. 폼을 제출하고나서 정렬 방식이 바뀌는 것을 방지하기 위해 sort 값도 넘겨줬습니다.
urls.py에서 url 경로도 작성해줍니다.
# comment_views.py
path('comment/create/<int:answer_id>/<str:sort>',
comment_views.comment_create, name='comment_create'),
path('comment/delete/<int:comment_id>/<str:sort>',
comment_views.comment_delete, name='comment_delete'),
마지막으로 동작을 수행하는 함수도 작성해 줍니다. 댓글을 작성한 후 다시 원래 있던 답변으로 돌아올 수 있도록 답변 기능을 구현할 때 배운 앵커 기능을 사용했습니다. 이때 앵커를 의미하는 #answer_{}가 맨 뒤에 있어야 정상적으로 작동했습니다.
comment_views.py
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.shortcuts import render, get_object_or_404, redirect, resolve_url
from django.utils import timezone
from ..forms import CommentForm
from ..models import Answer, Comment
@login_required(login_url='common:login')
def comment_create(request, answer_id, sort):
answer = get_object_or_404(Answer, pk=answer_id)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.author = request.user
comment.create_date = timezone.now()
comment.answer = answer
comment.save()
return redirect('{}?sort={}#answer_{}'.format(
resolve_url('pybo:detail', question_id=answer.question.id), sort, answer.id))
else:
form = CommentForm()
context = {'question' : answer.question, 'form' : form}
return render(request, 'pybo:/question_detail.html', context)
@login_required(login_url='common:login')
def comment_delete(request, comment_id, sort):
comment = get_object_or_404(Comment, pk=comment_id)
if request.user != comment.author:
messages.error(request, '삭제권한이 없습니다')
return redirect('{}?sort={}#answer_{}'.format(
resolve_url('pybo:detail', question_id=comment.answer.question.id), sort, comment.answer.id))
comment.delete()
return redirect('{}?sort={}#answer_{}'.format(
resolve_url('pybo:detail', question_id=comment.answer.question.id), sort, comment.answer.id))
위의 코드들로 구현된 댓글 기능 사진입니다.