Python - Django를 이용한 인스타그램 클론 - 9

2022. 11. 12. 17:14카테고리 없음

피드에 Like를 구현할 차례이다, 우선 피드에서 사용하지 않는 material icon을 없애주었다.

그리고 content - <views.py>에아래와 같이 like를 구현해주었다.

class ToggleLike(APIView):
    def post(self, request):
        feed_id = request.data.get('feed_id', None)
        is_like = request.data.get('is_like ', True)

        if is_like == "True" or is_like =="true":
            is_like = True
        else :
            is_like = False

        email = request.session.get('email', None)

        Like.objects.create(feed_id=feed_id, is_like=is_like, email=email)

        return Response(status=200)

if문의 내용은 is_like가 True이면, is_like 변수를 true로 설정, False면 false로 설정해 objects create를 해주는 것이다. 

그리고 models.py를 수정해주자. 우선 Feed 클래스 모델에 like_count는 필요없기 때문에(다른 테이블을 사용할거니까) 없애줘도 된다. 없애준다.

그리고 views.py에서, like_count를 직접 추가해주면 된다.

like_count = Like.objects.filter(feed_id=feed.id, is_like=True).count() # like_count 역할을 해준다
            feed_list.append(dict(
                            id=feed.id,
                            image=feed.image,
                            content=feed.content,
                            like_count=like_count,
                            profile_image=user.profile_image,
                            nickname=user.nickname,
                            reply_list=reply_list
                                  ))

그렇게 변수로 추가한 like_count를 feed_list에서도 feed.like_count가 아닌 우리가 만든 like_count로 보내주는 걸로다가 해주고 python3 make migrations

해주면 ! 실행은 된다.

이후에 DB에서 like 부분에 True 값을 하나 넣어주면 잘 출력되어야 한다.

이렇게 넣어주고, 접속해보면

1명이 좋아합니다라고 찍혔다.

앞에 '누구' 부분을 실제 이름으로 바꿔줘야 할 것 같다. 그리고 사용자가 feed를 좋아요 했는데도 material icon이 비어있기에 like가 true일 때는 가득 찬 하트로 보여져야 할 것이다. 

일단 누른 하트를 구현하기 위해 views에서 몇 가지 추가를 해주자.

class Main(APIView):
    def get(self, request):

        email = request.session.get('email', None)

        if email is None:
            return render(request, "user/login.html")

        user = User.objects.filter(email=email).first()  # 웹 서버 세션에 등록된 이메일 정보를 통해 유저 데이터 불러온다

        if user is None:
            return render(request, "user/login.html")

        feed_object_list = Feed.objects.all().order_by('-id') #feed_list에 Feed에 있는 모든 데이터를 가져오겠다는 것
        # 위 같이 Feed.objects.all()과 같은 라인이 쿼리셋 역할을 한다. 즉 sql쿼리 문에서 select * from content_feed와 같은 동작을 한다
        feed_list = []
        for feed in feed_object_list:
            user = User.objects.filter(email=feed.email).first()
            reply_obejct_list = Reply.objects.filter(feed_id=feed.id)
            reply_list = []
            for reply in reply_obejct_list:
                user = User.objects.filter(email=reply.email).first()
                reply_list.append(dict(
                    reply_content=reply.reply_content,
                    nickname=user.nickname
                )) # 댓글 목록들 추가

            like_count = Like.objects.filter(feed_id=feed.id, is_like=True).count() # like_count 역할을 해준다
            is_liked = Like.objects.filter(feed_id=feed.id, email=email, is_like=True).exists()

            feed_list.append(dict(
                            id=feed.id,
                            image=feed.image,
                            content=feed.content,
                            like_count=like_count,
                            profile_image=user.profile_image,
                            nickname=user.nickname,
                            reply_list=reply_list,
                            is_liked=is_liked
                                  ))

        return render(request, "minstagram/main.html", context=dict(feeds=feed_list, user=user)) # 딕셔너리 형태로 feed_list 데이터를 넘겨주기

is_liked를 만들어서 Like 모델에서 사용자 email을 가진 like가 true라면 exists(), 즉 True이게 해주었다. 그렇게 하고 feed_list에 append해주었다.

이제 main.html에 is_liked 값이 True이면 material icon이 가득 차게 해주면 되겠다.

<main.html>

                    <span class="material-icons-outlined">
                        {% if feed.is_liked %}
                            favorite
                        {% else %}
                            favorite_border
                        {% endif %}
                    </span>

위와 같이 feed의 is_liked가 True라면 꽉 찬 매터리얼 아이콘인 favorite를, False(Else)면 꽉 차지 않은 favorate_border을 표시하게 해주고 다시 확인해보면,

적용되었다. 색도 style값으로 빨간색으로 먹여주면 더 보기좋을 듯 하다.

그리고 직접 하트를 누르면 해당 값이 바뀌도록 해주기 위해서 click을 적용해주어야한다. 우선 span에 id값을 주고, 해당 id값이 눌리면 바뀌도록 제이쿼리를 짜주면 된다.

<span id="favorite_{{ feed.id }}" style="color:red" class="favorite material-icons-outlined">{% if feed.is_liked %}favorite{% else %}favorite_border{% endif %}</span>

위와 같이 id값을 favorite_피드아이디, class 앞부분에 favorite를 추가하고, 

하지만 새로고침하면 다시 초기화된다, 직접 DB의 값까지는 바뀌지 않아서이다. 이를 수정하기 위해서 우선은 html에 속성값으로 feed_id를 하나 만들어준다(없기때문에)

<span id="favorite_{{ feed.id }}" feed_id = "{{ feed.id }}" style="color:red; cursor:pointer;" class="favorite material-icons-outlined">{% if feed.is_liked %}favorite{% else %}favorite_border{% endif %}</span>

그리고 jquery부분에서 feed_id를 불러올 수 있게 된다.(아래를 추가)

let feed_id = event.target.attributes.getNamedItem('feed_id').value;

그리고 ajax값으로 값을 날려주면 된다. 그전에 like클래스의 url을 추가해줘야한다.

urlpatterns = [
    path('upload', UploadFeed.as_view()),
    path('reply', UploadReply.as_view()),
    path('like', ToggleLike.as_view()),
    path('profile', Profile.as_view()),
    path('main', Main.as_view())
]

jquery 부분은 아래와 같이 해주면 되겠다.

 $('.favorite').click(function (event){
        let feed_id = event.target.attributes.getNamedItem('feed_id').value;
        let favorite_id = event.target.id;
        let favorite_text = $('#'+favorite_id).html();
        console.log(favorite_text)

        if (favorite_text == "favorite"){
            $('#'+favorite_id).html("favorite_border");
        } else {
            $('#'+favorite_id).html("favorite");
        }

        $.ajax({
            url: "/content/like",
            data: {
                feed_id : feed_id,
                favorite_text : favorite_text
            },
            method: "POST",
            // datatype : json을 없앤 이유는 그냥 폼 아이디로 부터 가져오는 것이므로,
            success: function(data) {
                console.log("성공");
                },
            error: function(request, status, error){
                console.log("에러");
            },
            complete: function(){
                console.log("완료");
            }
        });

조금 설명을 하자면, feed_id를 만들어 구분, favorite_text의 내용에 따라 누르면 favorite(채워진 하트)일 때 favorite_border(안채워진 하트)로 변경, favorite_border(안채워진 하트)일 때 favorite(채워진 하트)로 바뀔 수 있는 if문을 작성한것이고, ajax로 like에다가 해당 정보를 쏘아 보냈다.

class ToggleLike(APIView):
    def post(self, request):
        feed_id = request.data.get('feed_id', None)
        favorite_text = request.data.get('favorite_text', True)

        if favorite_text == "favorite_border":
            is_like = True
        else :
            is_like = False

        email = request.session.get('email', None)

        Like.objects.create(feed_id=feed_id, is_like=is_like, email=email)

        return Response(status=200)

그리고 views도 조금 수정이 필요한데 아까 우리가 사용했던 is_like변수 대신 favorite_text로 바로 확인할 수 있도록 해주고 이에 따라 db내용도 True or False로 바뀌도록 설정해주었다.

구현해보면 db도 바뀌어 있는지 확인해보자.

- 눌렀는데, like가 없는 상태에서 누르면 적용은 되는데 like가 있는 상태에서 like가 사라지지 않는 버그가 보였다.

db의 값을 확인해보니까 새로운 열이 추가되어 발생한 버그같다.

views에서 기존에 사용자가 like한 피드라면 다시 create하지 않도록 예외처리를 해주자.

class ToggleLike(APIView):
    def post(self, request):
        feed_id = request.data.get('feed_id', None)
        favorite_text = request.data.get('favorite_text', True)

        if favorite_text == "favorite_border":
            is_like = True
        else :
            is_like = False

        email = request.session.get('email', None)

        like = Like.objects.filter(feed_id=feed_id, email=email).first()
        if like:
            like.is_like=is_like
            like.save()
        else:
            Like.objects.create(feed_id=feed_id, is_like=is_like, email=email)

        return Response(status=200)

like에 like를 한 사용자가 있는지 불러오고, 만약 있다면(if like) 바뀐 is_like를 적용, 없다면 새로운 like objects를 생성하도록 해준 후에, like 테이블의 값들을 모두 지워주고 다시 실행해보았다.

아무 like도 안누른 상태에서,
like를 누르고 새로고침 해주면
create는 잘 적용, 만약 여기서 like를 해제하면

해제도 잘 되었다.

북마크도 이런 like와 유사하다. 바로 만들어주면 되겠다. <views.py> ToggleLike 아래에 만들어준다.

class ToggleBookmark(APIView):
    def post(self, request):
        feed_id = request.data.get('feed_id', None)
        bookmark_text = request.data.get('bookmark_border', True)

        if bookmark_text == "bookmark_border":
            is_marked = True
        else :
            is_marked = False

        email = request.session.get('email', None)

        bookmark = Bookmark.objects.filter(feed_id=feed_id, email=email).first()
        if bookmark:
            bookmark.is_marked = is_marked
            bookmark.save()
        else:
            Bookmark.objects.create(feed_id=feed_id, is_marked=is_marked, email=email)

        return Response(status=200)

우선, 이름만 바꾼 상태이다. main에서 적용시켜준다.

<div>
	<span id="bookmark_{{ feed.id }}" feed_id = "{{ feed.id }}" style="cursor: pointer;" class="bookmark material-icons-outlined">{% if feed.is_marked %}bookmark{% else %}bookmark_border{% endif %}</span>
</div>

그런데 여기서 우리는 아직 is_marked를 만들지 않아서 다시 views.py로 가서 만들어준다.

is_marked = Bookmark.objects.filter(feed_id=feed.id, email=email, is_marked=True).exists()
            feed_list.append(dict(
                            id=feed.id,
                            image=feed.image,
                            content=feed.content,
                            like_count=like_count,
                            profile_image=user.profile_image,
                            nickname=user.nickname,
                            reply_list=reply_list,
                            is_liked=is_liked,
                            is_marked=is_marked
                                  ))

메인 클래스에 추가해줬다.

그리고 main.html에 jquery부분을 추가해준다.

    $('.bookmark').click(function (event){
        let feed_id = event.target.attributes.getNamedItem('feed_id').value;
        let bookmark_id = event.target.id;
        let bookmark_text = $('#'+bookmark_id).html();

        if (bookmark_text == "favorite"){
            $('#'+bookmark_id).html("bookmark_border");
        } else {
            $('#'+bookmark_id).html("bookmark");
        }

        $.ajax({
            url: "/content/bookmark",
            data: {
                feed_id : feed_id,
                bookmark_text : bookmark_text
            },
            method: "POST",
            // datatype : json을 없앤 이유는 그냥 폼 아이디로 부터 가져오는 것이므로,
            success: function(data) {
                console.log("성공");
                },
            error: function(request, status, error){
                console.log("에러");
            },
            complete: function(){
                console.log("완료");
            }
        });


    })

 

한번 실행해보자

여기서 북마크를 누르면,
새로고침 해도 잘 되어있다. DB에서도 잘 True 값이 들어간 것을 확인할 수 있다.

잘 작동하는 것을 확인할 수 있다

 

이제 대충 피드보여주는 부분은 된 것 같고, 프로필에서 내가 올린 글들을 가져올 수 있도록 해보겠다. 

그건 다음 글에서 설명하도록 하겠다.