1. Authentication 인증 시스템 구축
학습 목표 : 요청을 보내는 유저가 로그인을 했는지, 또 접근하는 페이지의 성격에 따라 해당 페이지의 유저 객체와 요청 유저 객체를 비교하고, 필요할 경우 요청을 차단하는 코드를 구축한다.
1) pragmatic/accountApp/views.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse, reverse_lazy
from accountApp.models import HelloWorld
from django.views.generic import CreateView, DetailView, UpdateView, DeleteView
from accountApp.forms import AccountUpdateForm
from django.http import HttpResponseForbidden
def hello_world(request):
class AccountCreateView(CreateView):
class AccountDetailView(DetailView):
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'
# 로그인 되어 있을 때만 Update 가능 + 타 계정으로는 접속 불가능
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'
# 로그인 되어 있을 때만 Delete 가능 + 타 계정으로는 접속 불가능
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()
Python
복사
2. Decorator를 이용한 코드 간소화
학습 목표 : 파이썬의 Decorator 패턴을 이용해 자주 쓰이는 코드들을 줄여본다. 장고에서 기본 제공하는 Decorator부터 커스터마이징 한 Decorator로 27강에서 만든 인증시스템 코드 볼륨을 줄인다.
Function
1) Decorator 적용 전
...
def hello_world(request):
if request.user.is_authenticated:
if request.method == "POST":
temp = request.POST.get('hello_world_input')
new_hello_world = HelloWorld()
new_hello_world.text = temp
new_hello_world.save()
hello_world_list = HelloWorld.objects.all()
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'))
...
Python
복사
2) Decorator 적용 후 (장고 기본 제공)
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()
hello_world_list = HelloWorld.objects.all()
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})
...
Python
복사
Method (class 내부)
1) 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'
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()
Python
복사
2) Decorator 적용 후 (장고 기본 제공)
※ @method_decorator(function, ‘적용할 method’)
: 일반 function에 사용하는 decorator를 method에 사용할 수 있도록 변환해주는 decorator
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
...
@method_decorator(login_required, 'get')
@method_decorator(login_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')
class AccountDeleteView(DeleteView):
model = User
context_object_name = 'target_user'
success_url = reverse_lazy('accountApp:login')
template_name = 'accountApp/delete.html'
Python
복사
직접 만든 Decorator 적용하기
1) pragmatic/accountApp/decorators.py (decorators.py 생성)
from django.contrib.auth.models import User
from django.http import HttpResponseForbidden
# (get, post 모두) pk를 확인해서, User.objects가 실제 request를 보낸 유저와 같은지 판별
def account_ownership_required(func):
def decorated(request, *args, **kwargs):
# 요청을 받으면서 pk(primary key)로 'pk' 값을 가지고 있는 유저가 user가 된다.
user = User.objects.get(pk=kwargs['pk'])
# user를 확인해서, 다른 경우 Forbidden 된다.
if not user == request.user:
return HttpResponseForbidden()
return func(request, *args, **kwargs)
return decorated
Python
복사
2) Decorator 적용 후 1 (직접 만든 Decorator 적용)
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from accountApp.decorators import account_ownership_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'
Python
복사
3) Decorator 적용 후 2 (직접 만든 Decorator 적용, 코드 간소화)
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from accountApp.decorators import account_ownership_required
# 배열로 만든다. (method_decorator에 넣으면, 배열 내에 있는 decorator들을 다 확인함)
has_ownership = [account_ownership_required, login_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'
Python
복사
3. superuser, media 관련 설정
학습 목표 : createsuperuser 명령어를 통한 관리자 계정 생성, 그리고 Media 파일을 다루기 위해 필요한 설정들을 진행한다.
1) superuser(관리자) 계정 생성
Username (leave blank to use 'jangs'): username입력
Email address: (생략 가능)
Password: password 입력
Password (again): password 입력
Superuser created successfully.
2) pragmatic/pragmatic/settings.py
step 1. 하단에 MEDIA_URL과 MEDIA_ROOT를 추가한다.
...
STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [
BASE_DIR / "static",
]
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_REDIRECT_URL = reverse_lazy('accountApp:hello_world')
LOGOUT_REDIRECT_URL = reverse_lazy('accountApp:login')
# 실제 미디어 파일에 접근하기 위해, 주소창에서 media ~(이하) 필요하다.
MEDIA_URL = '/media/'
# 미디어 파일을 서버에 올렸을 때, 어느 경로에 지정될 것인지에 대한 정보
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Python
복사
step 2. 터미널 창에서 $ pip install pillow 를 입력하여 설치한다.