작정하고 Django 28강 - Decorator를 이용한 코드 간소화

_·2023년 12월 28일

작정하고 Django 강의

목록 보기
27/44

Decorator 설명

  1. 함수마다 시작시간 종료시간을 확인
    앞뒤로 datetime.now()를 붙여주어 print 구문의 시작과 종료시간이 출력되게 함.
    반복적인 수작업이 되버림 -> 가독성 떨어짐.
  1. decorator 정의
    어떤 함수를 인자로 받아서 그 함수를 앞뒤(혹은 앞만, 뒤만)꾸며줄 수 있는 방식

  2. decorator 적용
    @decorator를 붙여주어 적용.

  3. 우리의 코드
    겹치는 부분을 decorator 를 이용할 것.

Decorator 를 이용하여 코드 수정

if request.user.is_authenticated: # 로그인을 한 경우else: # 로그인을 하지 않은 경우를 decorator로 만들어주기

hello_world 함수의 경우

from django.contrib.auth.decorators import login_required

...

@login_required # 추가
def hello_world(request):
    if request.method == "POST":
        temp = request.POST.get('hello_world_input')
        new_hello_world = HelloWorld()
        new_hello_world.text = temp
        new_hello_world.save()
        return HttpResponseRedirect(reverse('accountapp:hello_world'))
    else:
        hello_world_list = HelloWorld.objects.all()
        return render(request, 'accountapp/hello_world.html', context={'hello_world_list': hello_world_list})

테스트

로그인이 안되어있는 상태에서 hello_world로 이동
로그인 창으로 redirect 되는 것을 볼 수 있다 -> decorator가 작동하고 있다.

UpdateView 클래스의 경우

def get(self, *args, **kwargs):에서 get 은 클래스 안에 있는 메소드 이기 때문에 함수에서의 decorator 적용 방법과는 약간 다르다.
일반 함수를 사용하는 decorator 를 클래스 내부의 메소드에 사용할 수 있도록 변환

로그인 과정 구현

자신인지 확인하는 인증 과정은 아직 추가 안했음.

from django.utils.decorators import method_decorator
...
# 일반 함수를 사용하는 decorator 를 클래스 내부의 메소드에 사용할 수 있도록 변환해야한다 -> method_decorator
# 인자 : (일반 함수 decorator, 우리가 적용할 메소드(get, post 이런 식의 http 메소드를 의미))
@method_decorator(login_required, 'get')
@method_decorator(login_required, 'post')
class AccountUpdateView(UpdateView):
...

자신인지 확인하는 인증 과정을 추가하기 위해서 custom decorator를 하나 만들어야 함.
pragmatic/accountapp/decorators.py 파일 생성
다음과 같이 작성하면 이전 강의에서 작성했던 인증 시스템과 동일하게 작동

# pragmtic/accountapp/decorators.py
from django.contrib.auth.models import User
from django.http import HttpResponseForbidden

def account_ownership_required(func): # decorator 정의 (이 계정의 소유권이 필요하다는 이름)
    def decorated(request, *args, **kwargs): # request 를 받아
        # 우리가 원하는 작업 - 본인인지 확인하는 작업
        # get, post 등의 요청을 받으면서 pk 로 받은 값을 가지고 있는 User 객체가 user 가 되는 것
        user = User.objects.get(pk=kwargs['pk'])
        # pk 를 확인해서 그 User 객체가 실제로 request 를 보낸 user 와 같은지 아닌지 확인
        if not user == request.user: # 유저가 아니라면
            return HttpResponseForbidden()
        return func(request, *args, **kwargs) # 요청 보낸 유저와 유저 객체(pk값확인)가 같은 경우는 그냉 보내주기
    return decorated

#account_ownership_required 라는 decorator 를 만들었으니까 views.py에 적용하기
# pragmatic/accountapp/views.py
from accountapp.decorators import account_ownership_required

has_ownership = [account_ownership_required, login_required] # 하나의 배열로 만들어주기

...

@method_decorator(has_ownership,'get') # has_ownership 이라는 배열 내에 있는
@method_decorator(has_ownership,'post') # decorator 들을 모두 확인을 하고 체크 해줌.
class AccountUpdateView(UpdateView):
    model = User
    context_object_name = 'target_user'
    form_class = AccountUpdateForm
    success_url = reverse_lazy('accountapp:hello_world')
    template_name = 'accountapp/update.html'
    

@method_decorator(has_ownership,'get')
@method_decorator(has_ownership,'post')
class AccountDeleteView(DeleteView):
    model = User
    context_object_name = 'target_user'
    success_url = reverse_lazy('accountapp:login')
    template_name = 'accountapp/delete.html'

테스트

  1. 로그인을 하지 않은 상태에서 1번 계정의 탈퇴 페이지로 이동
    127.0.0.1:8000/accouns/delete/1을 입력하면 엑세스가 거부된다고 뜬다.

  2. 로그인 한 상태에서 1번 계정의 탈퇴 페이지로 이동
    마찬가지로 엑세스가 거부된다고 뜬다.

  3. 로그인 한 계정(3번)의 탈퇴 페이지는 정상적으로 이동
    127.0.0.1:8000/accouns/delete/3을 정상적으로 작동한다.

commit

git add ., git commit -m "django course 28 commit을 통해 커밋한다.

0개의 댓글