폼으로 댓글 기능 구현하기[2]
Posted on 2021-12-20 by GKSRUDTN99
Django로 웹사이트 만들기
Django
폼으로 댓글 기능 구현하기[2]
포스트 상세 페이지에 댓글 기능 반영하기
- 현재는 포스트 상세 페이지의 아랫부분에 목업 형태로 댓글이 나열되어 있고, 위에는 댓글 작성창도 있다.
- 우선, 아래 댓글들이 목업이 아니라 실제 댓글이 출력되도록 변경한다.
1. 포스트 상세 페이지의 현재 댓글 구조 확인하기
- 댓글과 관련이 있는 부분은 post_detail.html에서
<div class="comment-area">
안에 구현되어 있다. - class가 같은 요소는 한 HTML 문서 내에서 여러 개 등장할 수 있으므로 id="comment-area"를 div 태그에 추가할 것이다.
- 또한 댓글 역시 각각
<div>
태그로 구분되어 있지만 id는 부여되어 있지 않으므로, comment-뒤에 해당 comment의 pk를 붙이는 방식으로 id를 부여할 것이다.
2. 테스트 코드 작성하기
- test.py를 열어 setUp() 함수 맨 끝에 댓글을 만드는 코드를 추가한다.
from django.test import TestCase, Client
from bs4 import BeautifulSoup
# 아래에서 Comment Class Import
from .models import Post, Category, Tag, Comment
from django.contrib.auth.models import User
class TestView(TestCase):
def setUp(self):
# (...생략...)
self.post_003.tags.add(self.tag_python_kor)
self.post_003.tags.add(self.tag_python)
# 댓글 만들기
self.comment_001 = Comment.objects.create(
post=self.post_001,
author=self.user_obama,
content='첫 번째 댓글입니다'
)
test_post_detail
함수의 맨 끝에 Comment를 확인하는 내용을 추가한다.
# (... 생략 ...)
def test_post_detail(self):
self.assertEqual(self.post_001.get_absolute_url(), '/blog/1/')
response = self.client.get(self.post_001.get_absolute_url())
self.assertEqual(response.status_code, 200)
soup = BeautifulSoup(response.content, 'html.parser')
self.navbar_test(soup)
self.category_card_test(soup)
self.assertIn(self.post_001.title, soup.title.text)
main_area = soup.find('div', id='main-area')
post_area = main_area.find('div', id='post-area')
self.assertIn(self.post_001.title, post_area.text)
self.assertIn(self.post_001.category.name, post_area.text)
self.assertIn(self.post_001.author.username.upper(), post_area.text)
self.assertIn(self.post_001.content, post_area.text)
self.assertIn(self.tag_hello.name, post_area.text)
self.assertNotIn(self.tag_python.name, post_area.text)
self.assertNotIn(self.tag_python_kor.name, post_area.text)
# comment area
comments_area = soup.find('div', id='comment-area')
comment_001_area = comments_area.find('div', id=f'comment-{self.comment_001.id}')
self.assertIn(self.comment_001.author.username, comment_001_area.text)
self.assertIn(self.comment_001.content, comment_001_area.text)
# (...생략...)
3. post_detail.html 수정하기
- post_detail.html을 열어 댓글이 나타나는 부분을 다음과 같이 수정한다.
<!-- (...생략...) -->
<!-- Comments section-->
<div class="mb-5" id="comment-area">
<div class="card bg-light">
<div class="card-body">
<!-- Comment form-->
<form class="mb-4"><textarea class="form-control" rows="3"
placeholder="Join the discussion and leave a comment!"></textarea>
</form>
{% if post.comment_set.exists %}
{% for comment in post.comment_set.iterator %}
<!-- Single comment-->
<div class="d-flex" id="comment-{{ comment.pk }}">
<div class="flex-shrink-0"><img class="rounded-circle"
src="https://dummyimage.com/50x50/ced4da/6c757d.jpg"
alt="..."/></div>
<div class="ms-3">
<div class="fw-bold">{{ comment.author.username }} <small
class="text-muted">{{ comment.created_at }}</small></div>
{{ comment.content | linebreaks }}
</div>
</div>
{% endfor %}
{% endif %}
</div>
</div>
</div>
- 포스트에 댓글이 하나도 없으면 굳이 쿼리를 생성할 필요가 없으므로, 댓글의 유무를 먼저 확인한다.
- 댓글이 있다면 for문으로 반복해서 html 코드를 만든다.
- 댓글 본문에서 줄바꿈이 가능하도록
linebreaks
를 추가한다. - 이제 Django Test를 실행해보면 OK가 출력된다.
4. VIEW ON SITE버튼 만들기
- 관리자 페이지에서 댓글을 생성한 뒤 해당 댓글의 위치로 바로 이동하는 VIEW ON SITE버튼을 추가한다.
- Comment 모델에
get_absolute_url()
함수를 정의하여 해결할 수 있다. - models.py를 열어 다음과 같이 작성한다.
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f'{self.author}::{self.content}'
def get_absolute_url(self):
return f'{self.post.get_absolute_url()}#comment-{self.pk}'
- 뒤의
#comment-{self.pk}
는 html의 id를 의미한다. - 위와 같이 작성하면 포스트 상세페이지가 열린 뒤, 해당 id를 가진 태그로 페이지가 이동한다.
- 이제 관리자 페이지에서 Comment 하나를 선택하면, 해당 댓글이 있는 위치로 바로 이동할 수 있다.