장고의 폼 처리 과정
이미지 출처 : https://developer.mozilla.org/ko/docs/Learn/Server-side/Django/Forms
- 웹 브라우저에서 폼을 포함하는 페이지를 서버에 요청
- unbound 상태 : 폼의 각 필드는 비어있거나 초기값이 채워져 있지만 유저가 입력한 값이랑은 관련이 없다.
- binding : 입력된 데이터를 각 폼의 형식에 맞게 결합하는 작업.
서버 작업 : 해당 URL로부터 첫 요청인가? (binding된 데이터가 있는가?)
2.1. yes : unbound 상태의 기본 폼 형태를 만들어 다시 브라우저에 반환한다.
2.2. no : binding 된 데이터가 있다는 의미.2.2에서 -> 들어온 데이터가 유효한가? (데이터 유효성에 대한 검증)
3.1 yes : 유효데이터에 대해 이미 정의되어있는 동작을 수행한다.
3.2 no : 사용자가 입력한 데이터 + 문제가 발생한 필드에 대한 에러메시지를 표시함.2.1, 3.2에서 -> 반환 데이터와 함께 새로운 페이지를 표시하여 사용자에게 보내줌.
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 }}
부분처럼 값을 유연하게 사용하는것이 어렵다.
두가지 방식의 차이
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 로 보내는 등의 유연한 동작이 어려워보임
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를 할지를 선택해야함. (이는 장점이 될수도 있음.)
이는 두가지 방법중에 목적에 좀 더 맞는 방식으로 사용하는게 좋을듯 하다.