좋아요와 북마크 두 기능이 거의 유사하고 데이터베이스의 이름 정도와 활용 부분에서 조금 차이가 나는 정도이기 때문에
models.py
와 admin.py
를 작성해보도록 하겠습니다. 먼저
models.py
에 Like와 Bookmark 두 개의 클래스를 더 추가해보도록 하겠습니다. 두 클래스가 유사하기 때문에 Like를 만들고 Like를 토대로 Bookmark도 작성해보도록 하겠습니다. 파일명 :
post/models.py
... class Like(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) post = models.ForeignKey(Post, on_delete=models.CASCADE) created_at = models.DataTimeField(auto_now_add=True) updated_at = models.DataTimeField(auto_now=True) class Meta: unique_together = ( ('user' , 'post') ) class Bookmark(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) post = models.ForeignKey(Post, on_delete=models.CASCADE) created_at = models.DataTimeField(auto_now_add=True) updated_at = models.DataTimeField(auto_now=True) class Meta: unique_together = ( ('user' , 'post') )
터미널에서 아래와 같은 명령어를 입력합니다.
(venv)root@goorm:/workspace/instaclone/instaclone# python manage.py makemigrations

(venv)root@goorm:/workspace/instaclone/instaclone# python manage.py migrate

그리고 Post 클래스에 중개 모델(intermediate model)인 Like와 Bookmark를 담아줄 수 있는 필드를 만들어주도록 하겠습니다. 또한 좋아요 수와 북마크 수를 세주는 함수도 추가해주도록 하겠습니다.
파일명 :
post/models.py
class Post(models.Model): ... content = models.CharField(max_length=140, help_text = "최대 140자 입력 가능") like_user_set = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='like_user_set', through='Like') bookmark_user_set = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='bookmark_user_set', through='Bookmark') ... class META: ... @property def like_count(self): return self.like_user_set.count() @property def bookmark_count(self): return self.bookmark_user_set.count() def __str__(self): return self.content
그리고 터미널에 명령어를 통해서 수정된 사항을 적용해주도록 하겠습니다.
(venv)root@goorm:/workspace/instaclone/instaclone# python manage.py makemigrations

(venv)root@goorm:/workspace/instaclone/instaclone# python manage.py migrate

다음으로
admin.py
부분을 수정해보겠습니다.파일명 :
post/admin.py
from django from forms from django.contrib import admin from .models import Post, Like, Bookmark ... class LikeInline(admin.TabularInline): # 어드민 페이지 정리 model=Like @admin register(Post) class PostAdmin(admin.MoelAdmin): list_display = ['id', 'post', 'user', 'created_at'] # 보여주는 부분 list_display_links = ['author','post', 'user'] # 링크가 달리는 부분 form = PostForm inlines = [LikeInline] def nickname(request, post): return post.author.profile.nickname @admin.register(Like) class LikeAdmin(admin.ModelAdmin): list_display = ['id', 'post', 'user', 'created_at'] # 보여주는 부분 list_display_links = ['post', 'user'] # 링크가 달리는 부분 @admin.register(Bookmark) class BookmarkAdmin(admin.MoelAdmin): list_display = ['id', 'post', 'user', 'created_at'] list_display_links = ['post', 'user']
그리고 이제 admin 페이지에서 잘 적용이 되었는지 확인해보도록 하겠습니다.
(venv)root@goorm:/workspace/instaclone/instaclone# python manage.py runserver 0:80

Bookmarks, Likes가 추가된 것을 볼 수 있습니다.

Bookmark와 Like 개체가 잘 추가가 되는 모습을 볼 수 있습니다.


이번에는 서버에서 데이터를 불러와서 Ajax(Asynchronous JavaScript and XML) 통신을 통해 좋아요 값을 바꿔보도록 하겠습니다.

아래 그림과 같이 좋아요를 추가하여 하나의 포스트에 사용자 2명이 좋아요를 누른 상태를 가정해보겠습니다.

북마크도 마찬가지로 추가해보도록 하겠습니다.

post_list 페이지에서 좋아요와 북마크 표시가 되도록 post_list.html을 수정합니다.
article.contents 태그 아래에 div.bottom_icons와 div.likes.m_text 태그의 내용을 수정합니다.
파일명 :
post/templates/post/post_list.html
... <div class="bottom_icons"> <div class="left_icons"> <div class="heart_btn"> {% if user in post.like_user_set.all %} <div class="sprite_heart_icon_outline on" name="{{ post.id }}" data-name="heartbeat"></div> {% else %} <div class="sprite_heart_icon_outline" name="{{ post.id }}" data-name="heartbeat"></div> {% endif %} </div> <div class="sprite_bubble_icon"></div> <div class="sprite_share_icon" data-name="share"></div> </div> <div class="right_icon"> {% if user in post.bookmark_user_set.all %} <div class="sprite_bookmark_outline on" name="{{ post.id }}" data-name="bookmark"></div> {% else %} <div class="sprite_bookmark_outline" name="{{ post.id }}" data-name="bookmark"></div> {% endif %} <div class="likes m_text"> <span id="like-count-{{ post.id }}">좋아요{{ post.like_count}}개 </span> <span id="bookmark-count-{{ post.id }}"> 북마크{{ post.bookmark_count}} 개</span> </div> ...
잘 나온것은 볼 수 있지만 버튼기능은 작동하지 않는 것을 볼 수 있습니다. 앞서 프론트에서 했었던 델리게이션과 ajax를 통해서 버튼 기능을 구현해보도록 하겠습니다.

따로 델리게이션과 ajax를 포함한
script_ajax.html
파일을 만든 뒤 post_list.html
에 추가해주도록 하겠습니다.post/templates/post
폴더에 script_ajax.html
을 만듭니다.파일명 :
post/templates/post/script_ajax.html
<script type="text/javascript"> (function(){ const delegation = document.querySelector('.contents_box'); function delegationFunc(e){ let elem = e.target; console.log(elem); while(!elem.getAttribute('data-name')){ elem = elem.parentNode; if ( elem.nodeName ==='BODY'){ elem =null; return; } } if (elem.matches('[data-name="heartbeat"]')){ console.log('하트!') var pk = elem.getAttribute('name'); console.log(pk); $.ajax({ type: "POST", // 오류발생시 type -> method 로 url: "{% url 'post:post_like' %}", data: {'pk':pk, 'csrfmiddlewaretoken': '{{ csrf_token }}' }, dataType:"json", success: function (response){ alert('성공') var likeCount = document.querySelector('#like-count-'+pk); likeCount.innerHTML = '좋아요' + response.like_count +'개' }, error: function (request, status, error) { alert("code:"+request.status+"\n"+"message:"+request.responseText+"\n"+"error:"+error); }, }); }else if (elem.matches('[data-name="bookmark"]')){ console.log('북마크!'); var pk = elem.getAttribute('name'); console.log(pk); $.ajax({ type: "POST", url: "{% url 'post:post_bookmark' %}", data: {'pk': pk, 'csrfmiddlewaretoken': '{{ csrf_token }}' }, dataType: "json", success: function (response){ // alert('성공!'); var bookmarkCount = document.querySelector('#bookmark-count-'+pk); bookmarkCount.innerHTML = '북마크' + response.bookmark_count + '개'; }, error: function (request, status, error) { alert("code:"+request.status+"\n"+"message:"+request.responseText+"\n"+"error:"+error); }, }); } elem.classList.toggle('on'); } delegation.addEventListener('click',delegationFunc); })() </script>
post_list.html
에 script_ajax.html
을 추가하는 코드는 다음과 같습니다. content 블럭의 마지막 부분에 추가합니다.파일명 :
post/templates/post/post_list.html
... {% block content %} ... {% include "post/script_ajax.html" %} {% endblock %}
그리고 {% url 'post:post_like' %}와 {% url 'post:post_bookmark' %} 부분을 위해서
urls.py
부분에 두 URL을 추가해줍시다.파일명 :
post/urls.py
... urlpatterns = ... path('like/', post_like, name='post_like'), path('bookmark/', post_bookmark, name='post_bookmark'), ]
views.py
에 코드를 추가하여 뷰를 작성해줍니다.파일명 :
post/views.py
... import json from django.views.decorators.http import require_POST from django.http import HttpResponse ... @login_required @require_POST def post_like(request): pk = request.POST.get('pk', None) post = get_object_or_404(Post, pk=pk) post_like, post_like_created = post.like_set.get_or_create(user=request.user) if not post_like_created: post_like.delete() message = "좋아요 취소" else: message = "좋아요" context = {'like_count': post.like_count, 'message' : message} return HttpResponse(json.dumps(context), content_typ="application/json") @login_required @require_POST def post_bookmark(request): pk = request.POST.get('pk', None) post = get_object_or_404(Post, pk=pk) post_bookmark, post_bookmark_created = post.bookmark_set.get_or_create(user=request.user) if not post_bookmark_created: post_bookmark.delete() message = "북마크 취소" else: message = "북마크" context = {'bookmark_count': post.bookmark_count, 'message' : message} return HttpResponse(json.dumps(context), content_type="application/json")
잘 따라하셨다면 아래와 같이 좋아요 값이 변하는 것을 보실 수 있습니다.


서버와 통신해서 숫자가 바뀌고 있습니다. 이렇게 해서 좋아요와 북마크 기능을 최종적으로 완성을 하였습니다.