본문 바로가기

Web + APP/Django

Get, Post 요청을 모두 알아서 처리하는 FormView !

반응형
SMALL

안녕하세요 !

 

오늘은 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 ! 였습니다.

반응형
LIST

'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