본문 바로가기

Python/Django

[Django+Ajax/jQuery] '좋아요(likes)' 기능 구현하기 / 전체 웹페이지 새로고침 없이 좋아요 개수 업데이트하기

참고

초보몽키의 개발공부로그

Many-to-many relationships

stackoverflow: My own likes button

 

실습 저장소

https://github.com/JisunParkRea/djangotube_tutorial

 

JisunParkRea/djangotube_tutorial

Simple video service which can upload youtube videos using django - JisunParkRea/djangotube_tutorial

github.com


구현하고자 하는 기능

  • 마음에 드는 video에 '좋아요'를 누를 수 있게 하기
  • ajax를 사용하여 웹페이지 새로고침 없이 좋아요 개수를 비동기적으로 업데이트하기

models.py

  • Video는 여러 user로부터 likes를 받을 수 있고, user 또한 여러 Video에게 likes를 줄 수 있다: ManyToManyField
from django.conf import settings
from django.db import models
from django.conf import settings


class Video(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    title = models.CharField(max_length=100)
    video_key = models.CharField(max_length=12)
    likes_user = models.ManyToManyField(
        settings.AUTH_USER_MODEL, # this is preferred than just 'User'
        blank=True, # blank is allowed
        related_name='likes_user'
    ) # likes_user field

    def count_likes_user(self): # total likes_user
        return self.likes_user.count()

    def __str__(self):
        return self.title

urls.py

  • like button을 눌렀을 때 상호작용할 수 있는 url을 만들어야 한다.
from django.urls import path
from . import views


urlpatterns = [
    # ...
    path('like/', views.video_like, name='video_like'),
]

views.py

  • 로직을 구현해보자
    1. 로그인 한 사용자만이 likes button을 누를 수 있다: @login_required
    2. POST request만 받기로 하자: @require_POST
    3. likes button을 누르면 Video 모델의 likes_user 필드에 해당 user을 추가하자: .add()
    4. 이미 likes button을 누른 사용자가 또다시 누르면 좋아요 취소를 하자: .remove()
    5. ajax가 처리할 것은 좋아요의 개수와 알림 메세지이다. 이를 json 형태로 리턴하자: json.dumps()

 

import json
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST
from .models import Video


@login_required
@require_POST
def video_like(request):
    pk = request.POST.get('pk', None)
    video = get_object_or_404(Video, pk=pk)
    user = request.user

    if video.likes_user.filter(id=user.id).exists():
        video.likes_user.remove(user)
        message = '좋아요 취소'
    else:
        video.likes_user.add(user)
        message = '좋아요'

    context = {'likes_count':video.count_likes_user(), 'message': message}
    return HttpResponse(json.dumps(context), content_type="application/json")

template(video_detail.html)

<!-- ...생략 -->

<div>
    <input type="button" class="btn btn-info btn-sm like" name="{{ video.id }}" value="Like"
        style="margin-top: 7px">
    <p id="count-{{ video.id }}" style="font:bold 1em; margin-top: 3px">
        좋아요&nbsp;{{ video.likes_user.all.count }}개</p>
</div>

<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
    $(".like").click(function () { // .like 버튼을 클릭 감지
        var pk = $(this).attr('name')
        $.ajax({ // ajax로 서버와 통신
            type: "POST", // 데이터를 전송하는 방법
            url: "{% url 'video_like' %}", // 통신할 url을 지정
            data: { 'pk': pk, 'csrfmiddlewaretoken': '{{ csrf_token }}' }, // 서버로 데이터 전송시 옵션, pk를 넘겨야 어떤 video인지 알 수 있음
            dataType: "json",
            success: function (response) { // 성공
                alert(response.message);
                $("#count-" + pk).html("좋아요&nbsp;" + response.likes_count + "개"); // 좋아요 개수 변경
            },
            error: function (request, status, error) { // 실패
                alert("로그인이 필요합니다.")
                window.location.replace("/video/login/") // 로그인 페이지로 넘어가기
            },
        });
    })
</script>

결과