본문 바로가기

Python/Django

Django Form 작성 방법

Form 에 대한 필요한 내역을 정리하다 보니 필요한  내역이 길어져서 포스팅의 순서를 아래와 같이 정리 해 보았다.

 

 


  1. Get / Post 이란?
  2. Form 의 전송 Flow 
  3. Form 
  4. Model Form
  5. Validators

1. Get / Post 이란?

Form 을 알기 전에 서버에서 호출 방법인  Get / Post 에 대해 먼저 알아야 한다. 

참고 사이트 : https://www.w3schools.com/tags/ref_httpmethods.asp

 

HTTP Methods GET vs POST

W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, Python, SQL, Java, and many, many more.

www.w3schools.com

 

  • Get : 서버로부터 데이터 호출 시 URL뒤에 쿼리 스트링(Query String) 형태로 데이터를 전달하는 방식
  • Post : Get 방식과는 달리 URL에는 표시 되지 않고 서버로 전송하는 메세지 안(Body)에 메세지를 담아 호출하는 방식 

이 호출 방법들은  아래와 같은 특징 때문에 CRUD 중 Get 방식은 R을 Post방식은 CUD에서 사용 한다 

  GET POST
Visibility https://www.url.com/test?name1=value1&name2=value2 
데이터는 URL에 표기
https://www.url.com/test
URL에 데이터가 표시되지 않음
BACK button/Reload 문제가 되지 않음 데이터 재 전송 됨 
(브라우저는 데이터가 재 전송 예정임을 사용자에게 노티 필요)
북마크 추가 북마크 가능 북마크 불가
캐시 가능 여부 캐시 가능  캐시 불가
브라우저 기록 파라미터가 브라우저 기록에 남음 브라우저 기록에 남지 않음
데이터 길이 제한 데이터를 URL에 추가하기에  URL 길이가 제한됨(최대 URL 길이는 2048자) 제한 없음
데이터 유형에 대한 제한 사항 ASCII 만 사용 가능 제한 없음
Encoding type application/x-www-form-urlencoded pplication/x-www-form-urlencoded 
또는 multipart/form-data. 
이진 데이터에 멀티파트 인코딩 사용
보안 URL에 전송한 데이터가 포함됨.
때문에 비밀번호, 기타 민감한 정보를
보낼때 사용하면 안됨
매개변수가 브라우저 기록이나 
웹 서버 로그에 저장되지 않기 때문에 POST는 GET보다 안전

Form 에서 전송하는 내역도 그 필요성에 따라 Get이나 Post 로 호출하면 된다. 

<<!-- method 에 나와 있는 방식이 어떤 요청으로 보내겠다는 의미로 
      로그인 버튼을 누르면 form의 데이터를 post로 전송한다는 의미 -->>
<form method="post">
<button type="submit">로그인</button>
</form>

 

 

2. Form 의 전송 Flow 

  1. 사용자가 서버에 Form 양식 요청 (Get 으로 Request)
  2. 서버가 사용자에게 Form 양식 전송 ( Response - Unbound Form) 
  3. 사용자가 Form 에 데이터 입력후 Form 에 명시된 Method 방식으로 전송 (Post 로 Request) 
  4. 서버에서 입력된 데이터를 Data와 Form 을 합쳐(Binding) Bound Form 생성 
  5. 서버가 입력된 데이터가 유효한 데이터인지 체크하고 유효하지 않을 경우 사용자에게 유효한 데이터 재 요청 
  6. 유효한 데이터가 들어올때까지 2~5번 반복
  7. 입력된 Form 이 유효하면 서버의 작업을 수행
  8. 작업이 모두 끝나면 새로운 Resonse 전송 (Ex.새 페이지 등) 

 

3. Form 

장고에서 Form 을 생성하려면 form, view, url, html 의 4가지가 작업되어야 한다 

난 아래 순서대로 작업하였다.

 

본디 login 의 경우 django의 allauth 나 auth 에서 처리해주는 로직이 있어 아래 포스팅에서  불필요한 로직이 존재한다.

내 경우 포스팅에 쓸 수 없는  내역이 존재해서 가려서 작성한다. 

 

3-1. forms.py 

from django import forms

class LoginForm(forms.Form):
	email = forms.EmailField(max_length=50, label ='이메일')
    	password = forms.CharField(max_length=12,widget=PasswordInput,required=True,label ='비밀번호')
        portalid = forms.CharField(max_length=12, label = '포탈id')

form 에 해당하는 wiget 들에 대해 확인하고자 하면 아래 장고 사이트를 확인한다.

https://docs.djangoproject.com/en/3.2/ref/forms/fields/#built-in-field-classes

 

3-2. views.py

from .forms import LoginForm

def LoginView(request):
	login_form = LoginForm()
    	return render(reqeust, 'login.html', {'form':login_form})

 

3-3. login.html 

<form>
	{{form.as_ul}}
    <input type="submit" value = "전송">
</form>

as_ul, as_p, as_table 등은 모두 form 을 랜더링 하는 방식.

 

https://docs.djangoproject.com/en/2.2/ref/forms/api/#outputting-forms-as-html

 

3-4.  urls.py

from django.urls import path
from . import views

urlpatterns = [
	...
	path('/login/', views.LoginView, name='login'),
	...
 ]

 

4. Model Form

model과 form 이 비슷한 form 을 따로 생성할 필요 없이 model 값을 그대로 받아와서 form을 생성한다. 

html 과 url은 상단과 동일하게 사용가능  

 

4-1. forms.py 

class LoginForm(forms.ModelForm):
#사용할 모델을 작성 
    class Meta:
        model = User
        fields = ["email","password", "portalid"]
	# 모든 필드 일 경우 
        # fields = '__all__'

 

4-2. views.py 

내 경우 login 인시 변경되는 portal id값이 있어 함께 저장하도록 했다. 

def LoginView(request):
    if request.method == 'POST':
        #바인딩 
        login_Form = LoginForm(request.POST)
        #바운드 폼으로 모델 인스턴스 생성 > 모델 인스턴스를 처리 해줌 
        #데이터베이스에 저장 됨 
        user = login_form.save()
        
        return redirect('index')
    else:
        login_Form = LoginForm()
        return render(request, '/login.html', {'form': loginForm})

 

5. Validators

유효성 체크에 두가지 방법이 있다. 

  1. Model 기반에 유효성 체크 방법
  2. 옵션인자를 사용하여 유효성 체크 로직을 추가하는 방법 

 

5-1. Model 기반에 유효성 체크 방법

먼저, 모델 기반에 유효성 체크 하는 방법은 Model 내에 유효성을 체크로직을 삽입 하면 된다 

model.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=50, blank=False, unique=True, 
    						error_messages={'unique': '이미 있는 제목이네요!'})
    content = models.TextField()
    dt_created = models.DateTimeField(verbose_name="Date Created", auto_now_add=True)
    dt_modified = models.DateTimeField(verbose_name="Date Modified", auto_now=True)

    def __str__(self):
        return self.title

max_length=50 :  해당 data의 최대 길이  

blank=False : 해당 data를 빈칸으로 둬도 될지 (False - "" 값 허용 불가)

unique=True : 해당 data에 중복을 허용할지 (True- 중복 허용 불가) 

error_messages : 문제가 터질경우 나타낼 메세지 

             - 'unique' 규칙의 유효성이 맞지 않을 경우 에러메세지 노출

             - 'required' Form 필드의 기본 유효성 오류 메시지 변경시 작성  ex. {'required' : '비밀번호를 입력해 주세요'}

 

view.py 

form.is_valid 를 사용하여 유효성 데이터를 체크 한다 반환값은 Boolean 

def post_create(request):
    if request.method == 'POST':
        post_form = PostForm(request.POST)
        if post_form.is_valid():
            new_post = post_form.save()
            return redirect('post-detail', post_id=new_post.id)
            
    #get 방식 
    else:
        post_form = PostForm()
        return render(request, 'posts/post_form.html', {'form': post_form})

 

 

 

5-2. 옵션인자를 사용하여 유효성 체크 로직을 추가하는 방법 

별도로 validators.py 를 생성하여 만들 경우 여러 필드에서 사용이 가능하다. 

https://docs.djangoproject.com/en/3.2/ref/forms/fields/#built-in-field-classes

https://docs.djangoproject.com/en/2.2/ref/validators/

 

Validators | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

model.py 

from django.db import models
from django.core.validators import MinLengthValidator
from .validators import validate_symbols

class Post(models.Model):
    title = models.CharField(max_length=50, unique=True,
                                error_messages={'unique': '이미 있는 제목이네요!'})
    content = models.TextField(validators = 
    			[MinLengthValidator(10, '너무 짧군요! 10자 이상 적어주세요.'), 
                validate_symbols])
    dt_created = models.DateTimeField(verbose_name="Date Created", auto_now_add=True)
    dt_modified = models.DateTimeField(verbose_name="Date Modified", auto_now=True)

    def __str__(self):
        return self.title

 

validators.py

장고 내부에서 에러처리 중 인지하지 못한 건이 발생시 여기서 처리한다. 

#유효성 검증 실패 건 
from django.core.exceptions import ValidationError

def validate_symbols(value):
    if('@' in value) or ('#' in value):
        raise ValidationError('"@"와 "#"은 포함될 수 없습니다.')

 

forms.py 

모든 Form class는 cleaned_data 를 가지고 있다. 

cleaned_data 는 사용자가 입력한 데이터를 Dictionary 형태로 반환한다. 

form 필드를 작성할때 사용한 유효성검증이 끝난 데이터가 들어가져 있으나, model을 사용한 경우 유효성 검증이 사용되어 있지 않다.  때문에 아래처럼 작성하여 처리할 수 있다. 

from django import forms
from django.core.exceptions import ValidationError
from .models import Post

class PostForm(forms.ModelForm):

    class Meta:
        model = Post
        fields = ['title', 'content']

    def clean_title(self):
        title = self.cleaned_data['title']
        if '*' in title:
            raise ValidationError('* 는 포함될 수 없습니다.')
        return title

 

post.html 

form의 커스텀 에러메세지 표기

{%extends './base.html' %}
{% load static %}

<form method="post">{% csrf_token %}
    <h3>제목</h3>
    <p>{{form.title}}</p>
    {% for error in form.title.errors %}
        <p>{{error}}</p>
    {% endfor %}
    <h3>내용</h3>
    <p>{{form.content}}</p>
    {% for error in form.content.errors %}
        <p>{{error}}</p>
    {% endfor %}
    <input type="submit" value="전송"/>
</form>