decorator를 쓴다면?

Error maze·2022년 2월 13일
0

django

목록 보기
5/15

decorator를 쓰기 전 후를 보자.
얼마나 간단해지는지!

아래 코드는 회원정보 수정페이지의 views.py 파일의 코드이다.
회원정보에 대한 인증시스템(Authentication)을 구축하기 위해 get과 post method에서

  1. 로그인이 되어있다
  2. 로그인이 되어있는 user의 pk에 해당하는 유저객체(get_object)가 request를 보내는 user와 같은지 확인한다.

이 두가지의 조건을 만족했을 때 회원수정 페이지로 접근 가능하도록 만든 코드이다. 다른 pk에 해당하는 user가 접근하여 정보를 수정하는 것을 방지하기 위해 인증시스템은 필요하다.


from django.http import HttpResponseForbidden
from django.views.generic import UpdateView
from personapp.forms import AccountUpdateForm

class AccountUpdateView(UpdateView):
    model = User
    
    #이 form을 forms.py에서 커스텀마이징함.(아이디 수정불가의 기능을 만들기 위한 작업)
    form_class = AccountUpdateForm       
    
    context_object_name = "target_user"
    success_url = reverse_lazy("personapp:nice_world")
    template_name = "personapp/update.html"
    

#get
    def get(self, *args , **kwargs):
        # 로그인이 되어있다 
        # + 그 pk에 해당하는 유저객체(get_object)가 request를 보내는 user와 같은지 확인 
        #self는 view를 가리킴
        # 이는 다른 pk에 해당하는 user가 접근하여 탈퇴 및 업데이트를 방지하기 위함.
        if self.request.user.is_authenticated and self.get_object() == self.request.user:
            return super().get(*args, **kwargs)
            
        # 로그인이 되어있지 않다면
        else:
            return HttpResponseForbidden()

#post
    def post(self, *args , **kwargs):
        if self.request.user.is_authenticated and self.get_object() == self.request.user:
            return super().post(*args, **kwargs)
        else:
            return HttpResponseForbidden()

이 코드를 decorator로 줄인다면


@method_decorator(login_required, "get" )  
@method_decorator(login_required, "post" )
class AccountUpdateView(UpdateView):
    model = User
    form_class = AccountUpdateForm 
    success_url = reverse_lazy("personapp:nice_world")
    template_name = "personapp/update.html"
    context_object_name = "target_user"

@method_decorator(login_required, "get" ) 는 일반 function이 사용하는 decorator를 method에 사용할 수 있도록 변환한 코드이다.

일단 function에서는 @login_required 만 import하여 가져오면 인증시스템이 구축되지만 method에서는 @method_decorator를 사용하여 decorator와 하고자하는 method를 가져와야한다.

위 코드는 로그인이 되어있는지 까지만 확인한 decorator이고,(조건1)
이제 로그인된 user와 request를 보내는 user와 같은지 확인하는 (조건2) 작업이 필요하다. -> 커스텀마이징해야함.


만들었던 app폴더 내에 decorators.py파일을 생성하고 아래처럼 코드를 짠다.


from django.contrib.auth.models import User
from django.http import HttpResponseForbidden


def account_ownership_required(func):
    def decorated(request, *args , **kwargs):

        # pk를 가지고 있는 user object 를 user에 담음
        user = User.objects.get(pk=kwargs["pk"])

        # request를 보내는 user와 같은 지 확인
        if not user == request.user:
            return HttpResponseForbidden()
        return func(request, *args, **kwargs)
    return decorated

user = User.objects.get(pk=kwargs["pk"]) 는 pk를 가지고 있는 user object를 user라는 변수에 담는 다는 뜻이다.(로그인한 user)

그 user와 request를 보낸 user가 같은지 조건문을 통해 확인을 하고,
다르다면 서버에서 페이지 접근을 거부하도록 한다.


그리고 이 함수를 views.py에 적용시키면, 아래와 같은 코드가 되는데, 너무 복잡하고 기니까
@method_decorator(login_required, "get" ) 
@method_decorator(login_required, "post" )
@method_decorator(account_ownership_required, "get" )
@method_decorator(account_ownership_required, "post" )
class AccountUpdateView(UpdateView):
    model = User
    context_object_name = "target_user"
    form_class = AccountUpdateForm       
    success_url = reverse_lazy("personapp:nice_world")
    template_name = "personapp/update.html"
    

리스트의 형태(배열)로 만들어
has_ownership= [account_ownership_required, login_required]

배열 내에 있는 decorator들을 모두 확인하는 형식으로

코드를 줄일 수 있다.


@method_decorator(has_ownership, "get" ) 
@method_decorator(has_ownership, "post" )

profile
에러의 지옥 속 막다른 길 (ง •̀_•́)ง

0개의 댓글