Skip to content

Django 추천 기능

N:M 관계를 활용한 추천 기능


1. 데이터 모델 수정

추천 기능을 만드는 방법은 여러 가지가 있겠지만 models.py 파일을 아래와 같이 수정하여 질문 및 답변과 추천한 사용자를 ManyToMany로 연결하면 중복 추천을 방지할 수 있다.

models.py
from django.db import models
from django.contrib.auth.models import User

# Create your models here.


class Question(models.Model):
    """model for question"""

    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='question_user')
    subject = models.CharField(max_length=200)
    content = models.TextField()
    date_create = models.DateTimeField()
    date_modify = models.DateTimeField(null=True, blank=True)  # null is for DB, blank is for validation
    voter = models.ManyToManyField(User, related_name='question_voter')

    def __str__(self):
        return self.subject


class Answer(models.Model):
    """model for answer"""

    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='answer_user')
    question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='question_answers')
    content = models.TextField()
    date_create = models.DateTimeField()
    date_modify = models.DateTimeField(null=True, blank=True)
    voter = models.ManyToManyField(User, related_name='answer_voter')

    def __str__(self):
        return self.content

이 때 uservoter 필드에 대해 related_name 파라미터를 지정하지 않으면, User 모델의 중복 참조 오류가 발생한다.

모델을 변경한 후에는 마이그레이션을 진행해야한다.

manage.py makemigrations
manage.py migrate

2. view 생성

질문을 추천하기 위한 기능을 만들기 위해 views/question.py 파일에 아래 내용을 추가하자.

question.py
from django.shortcuts import get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages

from board_qna.models import Question


@login_required()
def question_vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    if request.user == question.user:
        messages.error(request, '본인이 작성한 글은 추천할 수 없습니다')
    else:
        question.voter.add(request.user)
    return redirect('board_qna:detail', question_id=question.id)  # type: ignore

답변을 추천하기 위한 기능을 만들기 위해 views/answer.py 파일에 아래 내용을 추가하자.

answer.py
from django.shortcuts import get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages

from board_qna.models import Answer


@login_required()
def answer_vote(request, answer_id):
    answer = get_object_or_404(Answer, pk=answer_id)
    if request.user == answer.user:
        messages.error(request, '본인이 작성한 글은 추천할 수 없습니다')
    else:
        answer.voter.add(request.user)
    return redirect('board_qna:detail', question_id=answer.question.id)  # type: ignore

3. URL 매핑

urls.py에 아래 내용을 추가하여 각 view를 호출하기 위한 URL을 만들어준다.

urls.py
from django.urls import path

from .views import *

app_name = 'board_qna'

urlpatterns = [
    path('question/vote/<int:question_id>/', question.question_vote, name='question_vote'),
    path('answer/vote/<int:answer_id>/', answer.answer_vote, name='answer_vote'),
]

4. 템플릿 수정

아래와 같이 question_detail.html 템플릿의 적당한 위치에 추천 버튼을 만들어주고, 추천 시 확인창을 띄우는 JavaScript를 연결해준다.

question_detail.html
{% block content %}
<button
  class="recommend btn btn-sm btn-outline-secondary"
  data-uri="{% url 'board_qna:question_vote' question.id %}">추천
<span class="badge rounded-pill bg-success">{{ question.voter.count }}</span>
</button>
<button
  class="recommend btn btn-sm btn-outline-secondary"
  data-uri="{% url 'board_qna:answer_vote' answer.id %}">추천
  <span class="badge rounded-pill bg-success">{{ answer.voter.count }}</span>
</button>
{% endblock %}
{% block script %}
  {% load static %}
  <script type="text/javascript" src="{% static 'recommend.js' %}"></script>
{% endblock %}

확인창을 띄우는 JavaScript의 내용은 아래와 같다.

recommend.js
const recommend_elements = document.getElementsByClassName("recommend");
Array.from(recommend_elements).forEach(function (element) {
  element.addEventListener(type='click', listener=function () {
    if (confirm(message="정말로 추천하시겠습니까?")) {
      location.href = this.dataset.uri;
    };
  });
});

아래와 같이 추천 기능이 정상적으로 작동하는 것을 확인할 수 있다.

django_recommend


Reference