FrameWork/pinterest clone

11. Subscriptionapp 만들기

mansoorrr 2023. 7. 25. 11:06

프로젝트를 구독할 수 있는 app을 만든다.

 

생각해보면 구독은 특이한 특징을 갖는다. 다른 페이지처럼 어디로 이동 하는 것이 아니라 그자리에서 구독을 누르고 구독을 취소하는 것으로 이루어진다. 이는 django에서 RedirectView라는 것을 이용한다.

 

지금까지 진행했던 것처럼 기본(모델 migrtate까지) 세팅을 진행한다.

구독은 유저가 프로젝트를 구독하는 형식으로 이루어진다. 따라서 필드는 user와 project두개가 되고 각각 User모델과 Project모델의 ForeignKey가 된다. 이후 한쌍으로 묶어준다.

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

from projectapp.models import Project


# Create your models here.
class Subscription(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='subscription')
    project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='subscription')
    
    # 쌍으로 묶어주기
    class Meta:
        unique_together = ('user', 'project')

 

1. RedirectView

django에서는 RedirectView라는 것을 제공한다. 이는 view요청시 아무일도 일어나지 않고 해당뷰로 다시 돌아가는 view이다. 구독은 프로젝트 페이지에서 진행된다.

 

조건 1: 프로젝트 페이지에서 구독버튼을 누르면 프로젝트 페이지로 돌아온다.

조건 2: 로그인한 사람만 구독버튼을 누를 수 있다.

 

조건 1을 만족시키기 위해서는 project/detail.html에서 project/detail.html로 돌아가야 한다. 그런데 detail은 project의 pk가 필요하다. 여기서는 pk를 어떻게 받아올 수 있을까? 버튼을 눌러 어떤 동작이 실행된다는것 = 요청이 있고 응답한다는것. 그렇다면 요청할때 pk를 실어서 보내면 응답에서 받을 수 있을것. 그럼 요청할때 어떻게 정보를 실어서 보낼 수 있을까? 이를위해 projectapp/detail.html에 내용을 추가한다.

 

<projectapp/detail.html>

redirect하는 구독버튼을 만들어주고 redirect할때 project_pk를 get으로 넘겨주게 세팅한다.

이렇게 하면 redirect시 detail페이지로 돌아갈때 필요한 pk부분을 만족시킬 수 있다.

# 로그인되어있다면
{% if user.is_authenticated %}
	# 구독버튼 보여주기
  <a href="{% url 'subscription:subscribe' %}?project_pk={{ target_project.pk }}">Subscribe</a>
{% else %}
	# 구독안함 보여주기
  <a href="{% url 'subscription:subscribe' %}?project_pk={{ target_project.pk }}">Unsubscribe</a>
{% endif %}

 

<views.py>

이후 저 pk를 받아와 실행할 수 있도록 함수를 작성한다.

from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.views.generic import RedirectView

from projectapp.models import Project
from subscriptionapp.models import Subscription


# Create your views here.
class SubscriptionView(RedirectView):
	# projectapp/detail.html의 project_pk정보 받아와 redirect할 수 있도록 설정
    def get_redirect_url(self, *args, **kwargs):
        return reverse('projectapp:detail', kwargs={"pk": self.request.GET.get('project_pk')})
	
    # 요청을 받은 정보가 모델에 있으면 모델에서 삭제, 없으면 모델에 저장
    def get(self, request, *args, **kwargs):
        user = self.request.user
        project = get_object_or_404(Project, pk=self.request.GET.get('project_pk'))
        subscription = Subscription.objects.filter(user=user, project=project)
        if subscription.exists():
            subscription.delete()
        else:
            Subscription(user=user, project=project).save()

        return super().get(request, *args, **kwargs)

 

<projectapp/detail.html수정>

해당 파일에서 모델에 저장되어 있을때와 저장되어있지 않을때를 구분하도록 수정한다.

{% if user.is_authenticated %}
  {% if not subscription %} # 모델에 없을때
  <a href="{% url 'subscription:subscribe' %}?project_pk={{ target_project.pk }}">Subscribe</a>
  {% else %} #모델에 있을때
  <a href="{% url 'subscription:subscribe' %}?project_pk={{ target_project.pk }}">Unsubscribe</a>
  {% endif %}
{% endif %}

 

<projectapp/views.py수정>

그럼 이제 projectapp에서도 subscription모델을 사용하게 되었으니 Project모델에 Mixin을 추가해 주어야 한다. 이미 Article모델을 Mixin해서 사용하고 있으니 Subscription모델을 사용할 수 있도록 내용을 추가한다.

    def get_context_data(self, **kwargs):
        object_list = Article.objects.filter(project=self.get_object())

        user = self.request.user
        project = self.object
        if user.is_authenticated:
            subscription = Subscription.objects.filter(user=user, project=project)
        else:
            subscription = None

        return super().get_context_data(object_list=object_list, subscription=subscription, **kwargs)

 

2. ListView

구독한페이지를 들어가면 내가 구독한 프로젝트들의 아티클이 나올수 있도록 한다.

이를위해 urls.py에 라우팅을 진행하고, 나머지 세팅을 진행한다.

 

SubscriptionListView는 article을 보여줄것이기 때문에 model은 Article을 사용하고 context_object_name은 article_list로 정해준다. 이후 구독한 프로젝트의 article을 가져오는 문법을 get_queryset함수를 통해 작성한다.

 

@method_decorator(login_required, 'get')
class SubscriptionListView(ListView):
    model = Article
    context_object_name = 'article_list'
    template_name = 'subscriptionapp/list.html'
    paginate_by = 5

    def get_queryset(self):
    	# Subscription 모델에서 user가 request.user인 project들 다 가져오기
        projects = Subscription.objects.filter(user=self.request.user).values_list('project')
        
        #Article의 project필드에서 projects에 와 같은 아티클들 다가져오기
        article_list = Article.objects.filter(project__in=projects) # sql에서 in문법을 django에서는 이렇게 씀
        return article_list

 

<subscriptionapp/list.html>

{% extends 'base.html' %}
{% load static %}

{% block content %}

<div>
  {% include 'snippets/list_fragment.html' with article_list=article_list %}
</div>

{% endblock %}

마지막으로 method_decorator를 통해 login_required를 적용시켜 준다.

<결과>