본문 바로가기
관리자

Programming-[Backend]/Django

Django로 Pinterest 따라 만들기-15. Subsrcibe, RedirectView, Field Lookup, queryset

728x90
반응형

 

1. SubscribeApp 시작

 

app 시작 및 View 작성

이번에는 어떤 Projecet를 구독할 수 있는 SubscribeApp을 만들어본다. app을 시작하고 기본 View를 만든다. 여기서 적용하는 RedirectView는 View 작업 후 바로 redirect가 일어나도록 해주는 view이다. 구독 후에는 구독 정보 및 버튼 모양만 변경되는 것이지, 별다른 로직이 없기 때문에 RedirectView를 적용한다.

 

@method_decorator(login_required, 'get')
class SubscriptionView(RedirectView):

    def get_redirect_url(self, *args, **kwargs):
        return reverse('proejctapp:detail', kwargs={'pk': self.request.GET.get('proeject_pk')})

    def get(self, request, *args, **kwargs):
        return super(SubscriptionView, self).get(request, *args, **kwargs)

 

 

Model 작성 : Unique Key 만들기

 

Subscription model을 만들고 migrate 한다. 구독은 project와 user가 일치해야만 할 수 있는 것으로 정의하였다. 여기서  어떤 속성값을 튜플로 묶어서 Meta 클래스에 정의하면 unique Key로 잡아준다. 

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')

 

migration이 완료된 subscribeapp_subscription 테이블의 DDL 일부

create index subscribeapp_subscription_project_id_7e6a4889
    on subscribeapp_subscription (project_id);

create index subscribeapp_subscription_user_id_0c0929e2
    on subscribeapp_subscription (user_id);

create unique index subscribeapp_subscription_user_id_project_id_3c6a6a34_uniq
    on subscribeapp_subscription (user_id, project_id);

 

View 로직 작성

get 요청이 왔을 때, Subscription이 토글 방식으로 작동되도록 로직을 작성한다. 여기서 get_object_or_404

메서드는 import 해오는 method로, 찾고자 하는 객체가 있으면 반환하고 없으면 404 에러를 띄워주는 단축 메서드이다.

 

user, project 정보를 바탕으로 subscription을 가져오고, if문을 사용하여 토글 방식으로 구독/구독 해제 기능을 만든다.

@method_decorator(login_required, 'get')
class SubscriptionView(RedirectView):

    def get_redirect_url(self, *args, **kwargs):
        return reverse('proejctapp:detail', kwargs={'pk': self.request.GET.get('project_pk')})

    def get(self, request, *args, **kwargs):
        project = get_object_or_404(Project, pk=self.request.GET.get('project_pk'))
        user = self.request.user
        subscription = Subscription.objects.filter(user=user, project=project)

        if subscription.exists():
            subscription.delete()
        else:
            Subscription(user=user, project=project).save()
            
        return super(SubscriptionView, self).get(request, *args, **kwargs)

 

projectapp/detail.html

프로젝트 상세 화면에서 subscription 접근이 가능하도록 링크를 넣어준다.

{# 중략... #}
<hr>

{# --- #}
<div class="text-center">
    {% if user.is_authenticated %}
    <a href="{% url 'subscribeapp:subscribe' %}?project_pk={{ target_project.pk }}"
    class="btn btn-primary rounded_pill px-4 mb-3" style="border-radius: 20rem;">
    Subscribe
    </a>
    {% endif %}
</div>
{# --- #}

{% include 'snippets/list_fragment.html' with article_list=object_list %}

 


 

2. Subscribe 버튼 구현, Field Lookup 과 queryset

 

Subscribe 버튼 토글 구현

프로젝트 detail 페이지에서 로그인 유저가 해당 프로젝트에 대한 구독 정보가 어떤지 판별하여 구독 중일때와 아닐 때 버튼을 다르게 표시한다. ProjectDetailView의 get_context_data 부분을 수정한다.

 

self.object, self.request.user를 통해서 project와 user 객체를 가져왔다. 그리고 Subscription.objects.filter()로 user, project에 해당하는 subscription만 가져온 후, get_context_data의 return 부분에 kwargs로 subscription = subscription으로 정보를 넘겨줬다. 이렇게 get_context_data의 반환값 정보가 template 쪽으로 넘어간다.

 

None : super() 에서 return 할 때 subscription 정보를 받으므로, 반드시 else: 구문을 사용하여 사용자가 로그인 하지 않았을때는 subscription에 None 값이 넘어가도록 해줘야한다.

def get_context_data(self, **kwargs):
    project = self.object
    user = self.request.user

    if user.is_authenticated:
        subscription = Subscription.objects.filter(user=user, project=project)
    else:
        subscription = None
    object_list = Article.objects.filter(project=self.get_object())
    return super(ProjectDetailView, self).get_context_data(object_list=object_list,
                                                           subscription=subscription,
                                                           **kwargs)

 

이제 넘어온 데이터를 바탕으로 버튼 토글 기능을 구현할 수 있도록 proejctapp/detail.html을 다시 수정한다.

 

SubscriptionListView : Field Lookup

구독 페이지에 들어가면, 사용자가 구독한 projects안에 있는 모든 article들을 보여주도록 SubscriptionListView를 작성한다.

 

여기서는 object에서 filter 조건을 좀 더 상세히 줄 수 있는 Field Lookup 문법을 적용해본다. Field Lookup은 SQL의 쿼리문을 적용했을 때와 동일한 결과가 나올 수 있도록 하는 django의 문법이다. 

https://docs.djangoproject.com/en/3.1/ref/models/querysets/#field-lookups

 

기본적으로 언더바 2개를 연속으로 사용한다. 위 링크의 공식 문서에 나온 예제를 아래에 기록한다. 아래 예시는 exact에 대한 구문이며 이외에도 like와 유사한 iexact, contains, in, gte, lte 등 다양한 문법들이 있다.

 

SubscriptionListView 생성

article들의 list가 출력되도록 해야하므로, model = Article으로 설정한다. 그리고 context_object_name='article_list'로 설정해서 이를 template에 넘겨준다.

 

get_queryset 메서드를 활용해서 template 에 넘겨줄 객체 리스트를 field lookup으로 찾는다. 여기서 values_list는 filter로 가져온 objects 중 특정한 속성을 지정하여 그 값들만 반환해준다.

 

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

    def get_queryset(self):
        user = self.request.user
        projects = Subscription.objects.filter(user=user).values_list('project')
        return Article.objects.filter(project__in=projects)

 

 

list.html 및 url, header 설정

subscription 페이지 내부에서 article_list를 표시할 수 있도록 list.html을 작성하고 subscriptionapp에서 url을 등록한다.

 

subscriptionapp/templates/subscriptionapp/list.html

{% extends 'base.html' %}

{% block content %}
    {% include 'snippets/list_fragment.html' with article_list=article_list %}
{% endblock %}

-url 등록 코드 생략

 

header에 subscription/list.html로 이동할 수 있는 link를 추가한다.

<a href="{% url 'subscribeapp:list' %}">
    <span>Subscription</span>
</a>

 

 


 

 

참조

1. 작정하고 장고! Django로 Pinterest 따라만들기 : 바닥부터 배포까지-박형석님 인프런 강의

https://www.inflearn.com/course/%EC%9E%A5%EA%B3%A0-%ED%95%80%ED%84%B0%EB%A0%88%EC%8A%A4%ED%8A%B8/dashboard

 
728x90
반응형