📢 공지사항
home

Section 3 (Authentication)

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_URLMEDIA_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 를 입력하여 설치한다.