Django에는 로그인 상태가 아니면 기능을 할 수 없게하는 방법 2가지가 있음
@login_required
사용is_authenticated
함수 사용 이 두개가 완전히 같은 기능을 하고 같은 용도로 쓰이는지 궁금해짐
결론
1,2 는 비슷한 기능을 함
하지만 요청을 어떻게 정의하냐에 따라, @login_required
가 구조적으로 처리하지 못하는 요청이 존재할 수 있다.
따라서, 요청에 따라 적절히 사용해야함
게시글 작성 or 삭제 요청을 처리하는 상황
게시글의 데이터에는 다음과 같이 user 정보가 들어간다
따라서 로그인 상태에서만 게시글을 쓰고 지울 수 있게 해야한다
이를 위해서
# articles/views.py
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST, require_GET
@login_required
@require_POST
def delete(request, pk):
article = Article.objects.get(pk=pk)
if request.user == article.user:
article.delete()
return redirect('articles:index')
return redirect('articles:detail', article.pk)
데코레이터 @login_required
를 사용해서 구현하려 했다
처음 생각은 @login_required
를 import 해서 로그인이 필요한 기능(게시글 작성, 삭제 등) 앞에 붙여주기만 하면
로그인을 하지 않으면 기능을 수행할 수 없도록 하는 기능을 간편하게 더해줄 수 있고
로그인상태가 아니면 로그인 페이지로 redirect해주기 때문에 매우 간편하게 구현할 수 있다고 생각했다
plus
네이버, 구글 같은 실제 홈페이지들을 생각해보면
1. 로그인이 필요한 기능을 클릭했을 때
2. 로그인 페이지로 넘어가고
3. 로그인 후 메인 페이지가 아닌 기존에 있던 페이지로 넘어가게 해준다.
이를 구현하기 위해 next
parameter 사용
또한,
이를 위해서 login.html을 수정해줘야 한다
next
를 사용하기 전엔
{% block content %}
<h1>Login</h1>
<form action="{% url 'accounts:login' %}" method="POST">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="로그인">
</form>
{% endblock content %}
위처럼 url을 지정 해줬지만
next
를 사용하려면 url을 비워줘야한다
http://127.0.0.1:8000/accounts/login/?next=/articles/create/
위처럼 next
유무에 따라AuthenticationForm
이 알아서
next
가 있으면 거기로 보내고 없으면
return redirect(request.GET.get('next') or 'articles:index')
에 써있는 것 처럼 'articles:index'
로 redirect
해주기 때문이다
{% extends 'base.html' %}
{% block content %}
<h1>Login</h1>
<form action="" method="POST">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="로그인">
</form>
{% endblock content %}
로그인 안한 상태의 메인페이지
비로그인 상태로 detail 페이지에서 게시글 삭제 시도
delete view
함수의 @login_required
로 인해 로그인 페이지로 리다이렉트 http://127.0.0.1:8000/accounts/login/?next=/articles/1/delete/
redirect로 이동한 로그인 페이지에서 로그인 진행
next
라는 쿼리파라미터에 담긴 /articles/1/delete/
로 GET
방식으로 요청 (redirect)
일단 articles/views.py
의 create
, delete
함수를 살펴보자
@login_required
@require_POST
def create(request):
if request.method == 'POST':
form = ArticleForm(request.POST, request.FILES)
if form.is_valid():
article = form.save(commit=False)
article.user = request.user
article.save()
return redirect('articles:detail', article.pk)
else:
form = ArticleForm()
context = {'form': form}
return render(request, 'articles/create.html', context)
@login_required
@require_POST
def delete(request, pk):
article = Article.objects.get(pk=pk)
if request.user == article.user:
article.delete()
return redirect('articles:index')
return redirect('articles:detail', article.pk)
위처럼 데코레이터 @require_POST
를 사용했다.
공식문서를 살펴보면
POST method로 들어오는 것만 처리해준다고 한다
근데 아까의 흐름을 떠올리면
1. 비로그인 상태
2. 로그인 필요한 기능 수행하려함
3. @login_required
가 로그인 페이지로 redirect
4. 로그인
5. next
에 담긴 곳으로 GET 방식으로 redirect
즉, 게시글 삭제, 작성의 기능은 @require_POST
를 사용해 POST method만 받도록 해놓았는데 @login_required
이후 GET method로 요청이 들어오니 405상태 코드 에러를 받게 된것이다
@login_required
는 정의되는 요청에 따라 적절하게 사용해야한다 (무지성 사용 금지)
@require_POST
를 사용하면서 위 문제를
if request.user is_authenticated:
사용해서 해결해보자
from django.views.decorators.http import require_POST, require_GET, require_http_methods
@login_required
@require_http_methods(['GET','POST'])
def create(request):
if request.method == 'POST':
form = ArticleForm(request.POST, request.FILES)
if form.is_valid():
article = form.save(commit=False)
article.user = request.user
article.save()
return redirect('articles:detail', article.pk)
else:
form = ArticleForm()
context = {'form': form}
return render(request, 'articles/create.html', context)
@require_POST
-> @require_http_methods(['GET','POST'])
GET, POST 둘다 받도록 수정
@require_POST
def delete(request, pk):
article = Article.objects.get(pk=pk)
if request.user.is_authenticated:
if request.user == article.user:
article.delete()
return redirect('articles:index')
return redirect('articles:detail', article.pk)
@login_required
삭제
+if request.user.is_authenticated:
사용