이번에는 게시글과 답변에 "추천(좋아요)" 기능을 추가한다
우선 게시글 모델에 추천인(voter) 속성을 추가해 보자.
하나의 게시글에 여러명이 추천할 수 있고 한 명이 여러 개의 게시글에 추천할 수 있으므로 이런 경우에는 "다대다(N:N)" 관계를 의미하는 ManyToManyField를 사용해야 한다.
boards\models.py 에 아래의 코드 추가
voter = models.ManyToManyField(User) # 추천인 추가
그런다음 makemigrations 명령을 실행

오류의 내용은 Post 모델에서 사용한 user와 voter가 모두 User 모델과 연결되어 있기 때문에 User.post_set 처럼 User 모델을 통해서 Post 데이터에 접근하려고 할 때 user를 기준으로 할지 voter를 기준으로 해야 할지 명확하지 않다는 오류이다.
이 문제는 위 오류의 HINT에서도 알 수 있듯이 related_name 인수를 추가하여 해결할 수 있다. 다음처럼 Post 모델을 변경해 보자.
user = models.ForeignKey(User, on_delete=models.CASCADE,related_name='user_post')
voter = models.ManyToManyField(User,related_name='voter_post')
user에는 related_name='user_post' 라는 인수를 지정하고 voter에는 related_name='voter_post' 라는 인수를 지정했다. 이렇게 하면 이제 특정 사용자가 작성한 질문을 얻기 위해서는
some_user.user_post.all() 처럼 사용할 수 있다. 마찬가지로 특정 사용자가 추천한 질문을 얻기 위해서는 some_user.voter_post.all() 처럼 사용할 수 있다.
some_user는 특정 사용자를 의미한다.
이제 마찬가지 방법으로 Comment모델에도 추천인(voter) 속성을 다음처럼 추가하도록 하자.
boards\models.py
# 댓글 관련 테이블
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,related_name='user_comment')
voter = models.ManyToManyField(User, related_name='voter_comment')
이제 makemigrations 명령과 migrate 명령을 실행하자.
Post 모델에 추천인 속성을 추가 했으니 이제 질문 추천 기능을 만들어 보자.
templates\boards\post_detail.html
(... 생략 ...)
<div class="badge bg-light text-dark p-2 text-start">
<div class="mb-2">{{ post.user.username }}</div>
<div>{{ post.created_at }}</div>
</div>
</div>
<div class="my-3">
<a href="javascript:void(0)" data-uri="{% url 'boards:post_vote' post.id %}"
class="recommend btn btn-sm btn-outline-secondary"> 추천
<span class="badge rounded-pill bg-success">{{post.voter.count}}</span>
</a>
{% if request.user == post.user %}
<a href="{% url 'boards:post_modify' post.id %}"
class="btn btn-sm btn-outline-secondary">수정</a>
<a href="javascript:void(0)" class="delete btn btn-sm btn-outline-secondary"
data-uri="{% url 'boards:post_delete' post.id %}">삭제</a>
{% endif %}
(... 생략 ...)
게시글의 추천 버튼을 질문의 수정 버튼 좌측에 추가했다. 그리고 버튼에는 추천수도 함께 보이도록 했다. 추천 버튼을 클릭하면 href의 속성이 javascript:void(0)으로 되어 있기 때문에 아무런 동작도 하지 않는다. 하지만 class 속성에 "recommend"를 추가하여 자바스크립로 data-uri에 정의된 URL이 호출되게 할 것이다. 이와 같은 방법을 사용하는 이유는 "추천" 버튼을 눌렀을때 확인창을 통해 사용자의 확인을 구하기 위함이다.
이어서 <추천> 버튼을 클릭했을 때 '정말로 추천하시겠습니까?'라는 확인 창이 나타나야 하므로 다음 코드를 추가
templates\boards\post_detail.html
제일 마지막에 추가
(... 생략 ...)
const recommend_elements = document.getElementsByClassName("recommend");
Array.from(recommend_elements).forEach(function(element) {
element.addEventListener('click', function() {
if(confirm("정말로 추천하시겠습니까?")) {
location.href = this.dataset.uri;
};
});
});
</script>
{% endblock %}
boards\urls.py
path('post/vote/<int:post_id>/', post_views.post_vote, name='post_vote'),
boards\views\post_views.py
@login_required(login_url='accounts:login')
def post_vote(request, post_id):
post = get_object_or_404(Post, pk=post_id)
if request.user == post.author:
messages.error(request, '본인이 작성한 글은 추천할수 없습니다')
else:
post.voter.add(request.user)
return redirect('boards:detail', post_id=post.id)
본인 추천을 방지하기 위해 로그인한 사용자와 추천하려는 질문의 글쓴이가 동일할 경우에는 추천할수 없게 하였다. 그리고 Post 모델의 voter는 여러사람을 추가할 수 있는 ManyToManyField이므로 post.voter.add(request.user) 처럼 add 함수를 사용하여 추천인을 추가한다.
본인 추천을 방지하기 위해 로그인한 사용자와 추천하려는 질문의 글쓴이가 동일할 경우에는 추천할수 없게 하였다. 그리고 Post 모델의 voter는 여러사람을 추가할 수 있는 ManyToManyField이므로 post.voter.add(request.user) 처럼 add 함수를 사용하여 추천인을 추가한다.
아래와 같이 확인해 보자

만약 본인이 작성한 질문을 추천할 경우에는 다음처럼 오류가 발생하게
된다

댓글 추천 기능은 게시글 추천 기능과 동일하므로 빠르게 작성해 보자.
templates\boards\post_detail.html
(... 생략 ...)
<div class="my-3">
<a href="javascript:void(0)" data-uri="{% url 'boards:comment_vote' comment.id %}"
class="recommend btn btn-sm btn-outline-secondary"> 추천
<span class="badge rounded-pill bg-success">{{comment.voter.count}}</span>
</a>
{% if request.user == comment.user %}
<a href="{% url 'boards:comment_modify' comment.id %}"
class="btn btn-sm btn-outline-secondary">수정</a>
<a href="#" class="delete btn btn-sm btn-outline-secondary "
data-uri="{% url 'boards:comment_delete' comment.id %}">삭제</a>
{% endif %}
(... 생략 ...)
아래를 추가
path('comment/vote/<int:comment_id>/', comment_views.comment_vote, name='comment_vote'),
boards\views\comment_views.py
@login_required(login_url='accounts:login')
def comment_vote(request, comment_id):
comment = get_object_or_404(Comment, pk=comment_id)
if request.user == comment.user:
messages.error(request, '본인이 작성한 글은 추천할수 없습니다')
else:
comment.voter.add(request.user)
return redirect('boards:detail', post_id=comment.post.id)
댓글에 추천수가 올라가는 것을 확인 하여 본다.
