Web/Django-python

Django의 form 핸들링 방법

장고의 폼 처리 과정

이미지 출처 : https://developer.mozilla.org/ko/docs/Learn/Server-side/Django/Forms

  1. 웹 브라우저에서 폼을 포함하는 페이지를 서버에 요청
  • unbound 상태 : 폼의 각 필드는 비어있거나 초기값이 채워져 있지만 유저가 입력한 값이랑은 관련이 없다.
  • binding : 입력된 데이터를 각 폼의 형식에 맞게 결합하는 작업.
  1. 서버 작업 : 해당 URL로부터 첫 요청인가? (binding된 데이터가 있는가?)
    2.1. yes : unbound 상태의 기본 폼 형태를 만들어 다시 브라우저에 반환한다.
    2.2. no : binding 된 데이터가 있다는 의미.

  2. 2.2에서 -> 들어온 데이터가 유효한가? (데이터 유효성에 대한 검증)
    3.1 yes : 유효데이터에 대해 이미 정의되어있는 동작을 수행한다.
    3.2 no : 사용자가 입력한 데이터 + 문제가 발생한 필드에 대한 에러메시지를 표시함.

  3. 2.1, 3.2에서 -> 반환 데이터와 함께 새로운 페이지를 표시하여 사용자에게 보내줌.

  4. 3.1에서 -> 동작 종료.

파일 작성

forms.py 의 선언 + 작성

#apitest/forms.py
from django import forms 
from django.http import HttpResponse , HttpResponseRedirect

class ContactForm(forms.Form):
    name = forms.CharField()
    message = forms.CharField(widget=forms.Textarea)
  • DB를 위한 model을 선언하는것과 유사하고 같은 필드타입, 매개변수등을 사용함.
  • 이미 사용하기 위한 다양한 formfield 들이 만들어져 있으니 그대로 가져다가 쓰기만 하면 됨.

urls.py 작성

  • 일반적인 view를 추가할 때 작성하는것과 동일하게 쓰면 된다.
  • 다만 views.py 에서 redirect URL에 대해 변경이 이뤄질 경우 그에 맞는 패턴을 추가해주어야 한다.

views.py 작성

  • 하나의 페이지에서 모든 작업을 처리하게 하려면, 위의 폼 처리과정을 생각해서 코드를 작성해야한다.
  • 함수형 view를 사용할 경우 메소드 결과에 따라 if문을 이용한 동작이 필요하지만, class형 generic 뷰를 이용할 경우 메소드에 따라 클래스 내에서 메소드를 정의하면 된다.
  • generic.FormView를 사용하면 : get/post 메소드를 사용할 필요도 없고 form_valid를 이용하면 된다.
  • form_class를 지정하여 forms.py에 선언된 form 중 어떤것을 사용할지를 정해주면 해당 templates에서 form.as_p 를 사용하여 미리 작성된 폼을 랜더링하여 화면에 띄워줄 수 있음.
  • templates에서 폼 데이터가 전송되어 오면 form_valid에서 해당 값을 처리할 수 있게 된다.
  • super().form_valid 의 역할은 render(request,템플릿 페이지, 인자값) 와 동일하다. 즉 , 지정한 페이지로 인자값을 가지고 이동한다는 의미.

templates 작성

<form method="post">{% csrf_token %}
    {{ form.as_p }}
    <input type="text" name="name"> 
    <input type="submit" value="Send message">
    {% if result %}
        name is : {{ result.name }} 
    {% endif %}
</form>
  • 위에서 말한 것 처럼 form.as_p를 사용하면 지정된 form을 가져와 랜더링해서 화면에 띄워주고, 해당 폼을 작성해 전송하면 POST로 값을 보낸다.
  • 위 코드는 formview와 view 방식 두가지를 섞은 결과인데, view 를 사용하면 form.as_p가 만들어주는 form을 이용하지 못하고, formview를 사용하면 {{ result.name }} 부분처럼 값을 유연하게 사용하는것이 어렵다.

두가지 방식의 차이

  1. generic.FormView의 사용

    class ContactView(generic.FormView): #contact
    template_name = 'apitest/contact.html'
    form_class = ContactForm
    success_url = reverse_lazy("apitest:formtest") #moving url when FORM request success.
    
    def form_valid(self,form):
        name = form.data.get('name')
        name2 = form.clean()
        return super().form_valid(form)
  • forms.py에 선언해놓은 form 모델을 그대로 가져다가 templates에서 사용할 수 있어 html 코드 작성을 간소화 할 수 있음.
  • get/post 값에 대한 처리를 비교적 간편하게 하여 model에 바로 저장할 수 있게끔 동작함.
  • 그러나 넘어온 메소드를 핸들링하여 다시 templates 로 보내는 등의 유연한 동작이 어려워보임
  1. generic.View의 사용

    class FormView(generic.View): #formtest
    template_name = 'apitest/contact.html'
    form_class = ContactForm
    #success_url = reverse_lazy("apitest:contact")
    
    def post(self,request):
        result = request.POST
        context ={
            'result' : result
        }
        return render(request,self.template_name,context)
    
    def get(self,request):
        result = request.GET
        context ={
            'result' : result
        }
        return render(request,self.template_name,context)
  • get/post 메소드를 핸들링하여 model에 저장하거나 templates 로 보내는 등 비교적 유연하게 사용할 수 있음.
  • html 코드로 form 태그를 직접 작성하는 수고가 필요함.
  • return형을 명시하여 render를 할지, redirect를 할지, response를 할지를 선택해야함. (이는 장점이 될수도 있음.)

이는 두가지 방법중에 목적에 좀 더 맞는 방식으로 사용하는게 좋을듯 하다.