decorator를 쓰기 전 후를 보자.
얼마나 간단해지는지!
아래 코드는 회원정보 수정페이지의 views.py 파일의 코드이다.
회원정보에 대한 인증시스템(Authentication)을 구축하기 위해 get과 post method에서
이 두가지의 조건을 만족했을 때 회원수정 페이지로 접근 가능하도록 만든 코드이다. 다른 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가 같은지 조건문을 통해 확인을 하고,
다르다면 서버에서 페이지 접근을 거부하도록 한다.
@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" )