안녕하세요 !
오늘은 Django Class Based View의 FormView에 대해서 알아보려고 합니다.
바로 시작하죠 !
제가 Login Form을 제작을 하면서 get요청(Login 창 들어올 때) 따로, post 요청(Login 시도)을 따로 처리를 했었더랬죠.
그게 당연한 줄 알았지만, 이 놈의 대단한 장고는 이를 알아서 처리해주는 FormView라는 친구가 있습니다 !
자세히 알아보죠.
우선 코드를 작성해봅시다.
views.py
from django.views.generic import FormView
from django.contrib.auth import authenticate
from .forms import LoginForm
class LoginView(FormView):
template_name = "users/login.html"
form_class = LoginForm
success_url = '/home/'
def form_valid(self, form):
username = form.cleaned_data.get("username")
password = form.cleaned_data.get("password")
user = authenticate(username=username, password=password)
if user is not None:
login(self.request, user)
return super().form_valid(form)
forms.py
class LoginForm(forms.Form):
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput)
네 다 작성해봤습니다. Login으로선 살짝 부족한 면도 있지만, 저희는 Login을 원하는게 아니잖아요 !
Form을 뜯어보도록 합시다.
우선 template_name은 Login Page를 뜻합니다. form_class는 저희가 만든 forms.py를 가져오고, success_url의 경우엔 Form의 post 요청이 잘 수행이 되면 이동하게되는 url이 될 수 있겠죠.
사실 이 부분까지는 제가 설명을 하지 않아도 여러분들이 다 아실 수 있을거라 생각합니다.
이게 바로 클래스 추상화의 힘이죠. 그 안에 뭐가 담겼는지 상관없이, 그냥 버튼 누르면 작동하는 기계처럼요 !
하지만, 저희는 성장을 원하는 개발자 좀 더 Deep하게 뜯어보도록 합시다.
우선 어떻게 get 요청과 post 요청을 안에서 수행하는지 궁금하시지 않으신가요 ? FormView 안으로 들어가서 FormView 코드를 보도록 합시다.
class FormView(TemplateResponseMixin, BaseFormView):
"""A view for displaying a form and rendering a template response."""
들어가보니 이렇게 적혀있네요? 주석을 읽어보시면, FormView는 form을 보여주고 template을 rendering을 해주는 View라고 적혀있습니다. 그래서 BaseFormView를 상속받고, TemplateResponseMixin을 상속받았나보군요 !
둘 다 뜯어봅시다.
우선 TemplateResponseMixin !
class TemplateResponseMixin:
"""A mixin that can be used to render a template."""
template_name = None
template_engine = None
response_class = TemplateResponse
content_type = None
def render_to_response(self, context, **response_kwargs):
"""
Return a response, using the `response_class` for this view, with a
template rendered with the given context.
Pass response_kwargs to the constructor of the response class.
"""
response_kwargs.setdefault('content_type', self.content_type)
return self.response_class(
request=self.request,
template=self.get_template_names(),
context=context,
using=self.template_engine,
**response_kwargs
)
def get_template_names(self):
"""
Return a list of template names to be used for the request. Must return
a list. May not be called if render_to_response() is overridden.
"""
if self.template_name is None:
raise ImproperlyConfigured(
"TemplateResponseMixin requires either a definition of "
"'template_name' or an implementation of 'get_template_names()'")
else:
return [self.template_name]
조금 복잡해졌습니다. 천천히 읽어볼까요?
우선 저희가 FormView에서 지정한 template_name 클래스 변수가 여기에 존재했네요 ! 이를 오버라이드(Overriding)해서 저희가 원하는 template에 위치하도록 만들었습니다.
음.. 나머지는 뭘까요. 우선 뒤로 미루고 하나 더 남은 BaseFormView를 뜯어봅시다.
class BaseFormView(FormMixin, ProcessFormView):
"""A base view for displaying a form."""
얘도 2개의 클래스를 상속받은 애였군요.
우선 FormMixin부터 봅시다.
class FormMixin(ContextMixin):
"""Provide a way to show and handle a form in a request."""
initial = {}
form_class = None
success_url = None
prefix = None
def get_initial(self):
"""Return the initial data to use for forms on this view."""
return self.initial.copy()
def get_prefix(self):
"""Return the prefix to use for forms."""
return self.prefix
def get_form_class(self):
"""Return the form class to use."""
return self.form_class
def get_form(self, form_class=None):
"""Return an instance of the form to be used in this view."""
if form_class is None:
form_class = self.get_form_class()
return form_class(**self.get_form_kwargs())
def get_form_kwargs(self):
"""Return the keyword arguments for instantiating the form."""
kwargs = {
'initial': self.get_initial(),
'prefix': self.get_prefix(),
}
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs
def get_success_url(self):
"""Return the URL to redirect to after processing a valid form."""
if not self.success_url:
raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
return str(self.success_url) # success_url may be lazy
def form_valid(self, form):
"""If the form is valid, redirect to the supplied URL."""
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form):
"""If the form is invalid, render the invalid form."""
return self.render_to_response(self.get_context_data(form=form))
def get_context_data(self, **kwargs):
"""Insert the form into the context dict."""
if 'form' not in kwargs:
kwargs['form'] = self.get_form()
return super().get_context_data(**kwargs)
오.. 좀 뭔가 많군요. 여기서 저희가 익숙한 메소드, 클래스 변수를 찾을 수 있습니다. 바로 success_url과 form_valid입니다.
form_valid 메소드의 경우엔 HttpResponseRedirect를 하는 함수를 사용하네요? Redirect시키는 애로 알 수 있겠죠. 그 안에 변수로 get_success_url()이 존재하는데 이 애의 경우엔 URL의 위치가 제대로 되어있는지 확인하는 함수로서 success_url 클래스 변수를 가져와서 확인을합니다.
즉, success_url을 잘 못 입력하면 form_valid에서 걸러지겠네요. 만약 success_url에 문제가 없다면 그 곳으로 redirect가 되는 거구요 !!
어느정도 이해가 되기 시작했습니다.
하지만 아직 저희는 get요청과 post요청을 어디서 하는지 알 수 없었습니다. 그렇다면 남은 마지막 ProcessFormView를 보도록 합시다 !
class ProcessFormView(View):
"""Render a form on GET and processes it on POST."""
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
# PUT is a valid HTTP verb for creating (with a known URL) or editing an
# object, note that browsers only support POST for now.
def put(self, *args, **kwargs):
return self.post(*args, **kwargs)
코드에서 보시다시피 get 요청과 post 요청을 ProcessFormView에서 하는 것을 확인할 수 있습니다. post에서 form이 유효한지 확인도 해주네요.
제가 아직 이해하지 못한거는 어떻게 상속받지 않았는데 다른 mixin method를 사용하는지 모르겠네요 ;;
이거는 제가 좀 더 알아보고 수정해보도록 하겠습니다.
중요한 건 FormView에서 get, post 요청을 처리하고, formvalid를 처리해주는 것을 확인할 수 있었다는 점입니다.
오늘 FormView를 뜯어보는 시간을 가졌습니다.
아직 이 FormView의 실행 자체가 어떻게 되는지는 잘 모르겠습니다. 어떻게 instance를 만들어서 실행하는지 궁금하네요 !
Django.. 너란 녀석..
이상 Get, Post 요청을 모두 알아서 처리하는 FormView ! 였습니다.
'Web + APP > Django' 카테고리의 다른 글
Class Based View (0) | 2020.05.10 |
---|---|
Managers와 QuerySets의 이해 (0) | 2020.05.02 |
추상 모델 (0) | 2020.04.29 |
커스텀 유저 모델 (0) | 2020.04.29 |
Django Setting 하기 ! (0) | 2020.04.18 |