
로그인하지 않은 사용자가 접근할 때, 로그인 페이지로 리다이렉트함
# views.py
def hello_world(request):
if request.user.is_authenticated:
if request.method == "POST":
text = request.POST.get("hello_world_input")
# DB 저장
new_hello_world = HelloWorld()
new_hello_world.text = text
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})
else:
return HttpResponseRedirect(reverse('accountapp:login'))
@login_required 데코레이터 사용login_required 데코레이터 특징
from django.contrib.auth.decorators import login_required
@login_required
def hello_world(request):
# request.user.is_authenticated 비교 로직 제거함
if request.method == "POST":
text = request.POST.get("hello_world_input")
# DB 저장
new_hello_world = HelloWorld()
new_hello_world.text = text
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})
1) 간단한 테스트 페이지 만들기
# view.py
def test(request):
return render(request, 'accountapp/test.html')
<!-- template -->
{% extends "base.html" %}
{% block content %}
<div style="text-align: center;">
<h1>TEST PAGE</h1>
</div>
{% endblock %}
# urls.py
from django.urls import path
from accountapp.views import hello_world, test
app_name = "accountapp"
urlpatterns = [
path('hello_world/', hello_world, name='hello_world'),
path('test/', test, name='test'),
]

2) 로그인페이지가 아닌 다른 페이지(위의 테스트 페이지)로 리다이렉트 시키기
@login_required 데코레이터 사용login_url 설정# views.py
from django.contrib.auth.decorators import login_required
@login_required
def hello_world(request):
if request.method == "POST":
text = request.POST.get("hello_world_input")
# DB 저장
new_hello_world = HelloWorld()
new_hello_world.text = text
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})
# settings.py
LOGIN_URL = reverse_lazy('accountapp:test')
3) 테스트
로그인 되지 않은 상태에서 /accounts/hello_world url로 접근하면 /accounts/test url로 리다이렉트 됨
reverse, reverse_lazy 공통점
장고의reverse,reverse_lazy함수는 URL 이름을 바탕으로 URL을 동적으로 생성해주는 역할을 한다.
reverse, reverse_lazy 차이점 (호출시점 반환방식에 차이 O)
(1) reverse:
호출 시 바로 URL을 반환해야 한다. 따라서 reverse를 사용하기 위해서는 호출 시점에 URLConf가 완전히 로드되어 있어야 한다.
(2) reverse_lazy:
URL을 즉시 생성하지 않고, 필요할 때까지 생성을 지연한다.
settings.py에서 reverse_lazy를 사용하는 이유는 django의 로딩순서 때문이다. settigns.py는 프로젝트의 구성 설정을 포함하며, 프로젝트가 실행될 때 가장 먼저 로드된다. 때문에 설정파일이 완전히 로드되기 전까지는 URLConf가 완전히 로드되지 않을 수 있다. reverse함수의 경우 URLConf가 완전히 로드가 되어야만 정상적으로 사용할 수 있기 때문에 settigns.py에서는 사용하지 않는다.
AccountUpdateView: 회원 정보 계정 수정AccountDeleteView: 회원 계정 삭제로그인한 상태로 접근을 했는지 여부와 로그인한 유저와 접근하려는 대상의 정보가 일치하는지 여부를 확인한다. (나는 내 정보만 수정하거나 삭제가 가능해야 한다. 남의 정보를 조회할 수 없도록 해야 함)# views.py
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'
def get(self, *args, **kwargs):
if self.request.user.is_authenticated and self.get_object() == self.request.user:
return super().get(*args, **kwargs)
else:
return HttpResponseForbidden("허용되지 않는 접근입니다.")
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("허용되지 않는 접근입니다.")
class AccountDeleteView(DeleteView):
model = User
context_object_name = 'target_user'
success_url = reverse_lazy('accountapp:login')
template_name = 'accountapp/delete.html'
def get(self, *args, **kwargs):
if self.request.user.is_authenticated and self.get_object() == self.request.user:
return super().get(*args, **kwargs)
else:
return HttpResponseForbidden("허용되지 않는 접근입니다.")
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("허용되지 않는 접근입니다.")
login_required: 장고에서 지원하는 함수로, 로그인한 상태로 접근하고 있는지 확인함account_ownership_required: 로그인한 유저와 접근하려는 대상이 동일한지 확인method_decorator를 이용하면 클래스 메서드에 데코레이터를 적용시킬 수 있다.@method_decorator(데코레이터함수, '클래스메서드')# decorator.py
from django.contrib.auth.models import User
from django.http import HttpResponseForbidden
# 로그인한 유저와 접근하려는 대상의 정보가 일치하는지 여부 확인
def account_ownership_required(func):
def wrapper(request, *args, **kwargs):
user = User.objects.get(pk=kwargs["pk"])
if request.user == user:
return func(request,*args, **kwargs)
else:
return HttpResponseForbidden("허용되지 않는 접근입니다.")
return wrapper
# views.py
from django.contrib.auth.decorators import login_required
@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('accountapp:hello_world')
template_name = 'accountapp/update.html'
@method_decorator(login_required, 'get')
@method_decorator(login_required, 'post')
@method_decorator(account_ownership_required, 'get')
@method_decorator(account_ownership_required, 'post')
class AccountDeleteView(DeleteView):
model = User
context_object_name = 'target_user'
success_url = reverse_lazy('accountapp:login')
template_name = 'accountapp/delete.html'
login_required, account_ownership_required 데코레이터를 사용한다.has_ownership 변수와 같이 데코레이터 함수를 리스트에 담아서 사용하면 보다 간결하게 사용할 수 있다.# views.py
from django.contrib.auth.decorators import login_required
# login_required 먼저 순차적으로 실행됨
has_ownership = [login_required, account_ownership_required]
@method_decorator(has_ownership, 'get')
@method_decorator(has_ownership, 'post')
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'