본문 바로가기
Developer/Django

[Django] Form 이해 및 사용 방법

by Doony 2020. 11. 30.

본 포스팅은 패스트캠퍼스 파이썬 강의를 들으며 배운 내용을 요약한 것입니다.
바로 전 포스팅에서 MTV의 T인 template을 상속하는 방법에 대해 알아봤습니다. 목적은 흔히 쓰이는 구조는 html 상속 기능을 통해 효율적으로 코드를 구성하는 것이었다고 말씀드렸습니다.


FORMS

HTML에 form으로 시작되는 그룹이 있습니다. 예를 들어 지난 포스팅에서 로그인 시 필요한 아이디와 비밀번호 입력 부분들이 form의 예시입니다.
만약 이런 폼이 여러개가 되고, 경우에 따라 서버에서 데이터를 불러와 표현해야한다면 어떻게해야할까요??
일일이 HTML에 칸을 미리 만들어두기에는 한계가 있을 것입니다. Django에서는 Forms.py라는 파일을 통해 뷰의 폼을 관리할 수 있습니다.


사용 방법

사용법은 다음 순서로 진행합니다.

  • Forms.py 파일 생성
  • HTML 에 Forms 부분 대체하기
  • Views.py에 폼 관련 구문들 삭제

회원가입을 예제로 폼을 구현해보겠습니다.


Forms.py 생성


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from django import forms
from .models import DoonyUsers
from django.contrib.auth.hashers import check_password
 
 
class LoginForm(forms.Form):
    username = forms.CharField(max_length=64,
                               label="사용자 이름",
                               error_messages={'required''아이디를 입력해주세요.'})
    password = forms.CharField(widget=forms.PasswordInput,
                               label="비밀번호",
                               error_messages={'required''비밀번호를를 력해주세요.'})
 
    def clean(self):
        cleaned_data = super().clean()
        username = cleaned_data.get('username')
        password = cleaned_data.get('password')
        if username and password:
            user = DoonyUsers.objects.get(username = username)
            if not check_password(password, user.password):
                self.add_error('password''비밀번호가 틀렸습니다.')
 
            else:
                self.user_id = user.id
cs

폼 내에서 CharField 선언을 통해 입력받을 폼을 작성합니다. 비밀번호의 경우, widget을 통해 입력받을 수 있습니다. 레이블링을 통해 HTML에서 보여줄 이름을 정할 수 있습니다.


clean은 다음 기능을 합니다.

  • 모델에서 유저 테이블을 불러와 비교 작업
  • 각종 에러 처리들

즉, 기존에 views.py에서 처리할 수 있는 작업을 가져왔다고 볼 수 있습니다. 폼 자체 및 폼에서 발생가능한 에러들은 모두 forms.py에서 작업한다고 볼 수 있습니다. 이렇게 해야 진정한 MTV? 패턴인가봅니다.


Views.py

모든 에러처리를 forms에서 가져갔기 때문에 상대적으로 단순합니다. 폼 관련을 모두 제외하고, 실제 비즈니스 로직들만 담을 수 있게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
def login(request):
    if request.method == 'GET':
        form = LoginForm
    elif request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            request.session['user'= form.user_id
            return redirect('/')
 
 
    return render(request, 'login.html', {'form': form})
cs
___

기존 코드는 아래와 같았습니다. 얼마나 단순해졌는지 보이시나요?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def login(request):
    if request.method == 'GET':
 
 
        return render(request, 'login.html')
    elif request.method == 'POST':
        username = request.POST.get('username'None)
        password = request.POST.get('password'None)
        res_data = {}
        if not(username and password):
            res_data['error'= '모든 값을 입력해야합니다.'
        else:
            duser = DoonyUsers.objects.get(username = username)
            if check_password(password, duser.password):
                # 일치
                request.session['user'= duser.id
                return redirect('/')
            else:
                # 에러
                res_data['error'= '비밀번호가 틀렸습니다.'
 
 
 
        return render(request, 'login.html', res_data)
cs

HTML 편집

마지막으로 HTML은 기존의 중복된 form들을 모두 제거하고, 단순히 forms.py를 불러오는 것만으로 구현이 가능합니다. 이렇게 하다보니, 안드로이드의 RecyclerView나, iOS의 CollectionView와 원리가 통하는 것 같아서 뭔가 깨달음이 옵니다. 아! 이것이 아키텍쳐! 패턴이구나!


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<title>회원가입</title>
 
 
{% extends "base.html" %}
 
{% block contents %}
<div class="row mt-5">
    <div class="col-12 text-center">
        <h1>로그인</h1>
    </div>
</div>
<div class="row mt-5">
    <div class="col-12 text-center">
        {{ error }}
    </div>
</div>
 
<div class="row mt-5">
    <div class="col-12">
        <form method="post" action=".">
            {% csrf_token %}
            {% for field in form %}
             <div class="form-group">
                 <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                 <input type="{{ field.field.widget.input_type }}"
                        class="form-control"
                        id="{{ field.id_for_label }}"
                        placeholder="{{ field.label }}"
                        name="{{ field.name }}">
 
 
             </div>
 
            {% if field.errors %}
            <span style="color: red">{{ field.errors }}</span>
            {% endif %}
            {% endfor %}
            <button type="submit" class="btn btn-primary">등록</button>
        </form>
    </div>
</div>
{% endblock %}
cs

이렇게 Forms의 사용방법에 대해 간단히 알아봤습니다.

댓글