Django Form과 ModelForm으로 데이터 검증 및 폼 생성 자동화하기

웹 애플리케이션에서 회원가입이나 설문조사와 같은 사용자 입력을 처리할 때, 입력 값의 유효성을 검증하고 오류 메시지를 표시하는 것은 필수적입니다. Django의 Form과 ModelForm 컴포넌트는 이러한 작업을 자동화하여 개발 생산성을 크게 향상시킵니다.

Form 컴포넌트 기본 기능

Form 클래스 정의하기

Form 컴포넌트는 Django의 forms 모듈을 상속받아 정의합니다. ModelForm과 유사하게 각 필드의 데이터 타입과 제약 조건을 선언할 수 있습니다.

from django import forms

class UserRegistrationForm(forms.Form):
    nickname = forms.CharField(max_length=10, min_length=3)
    level = forms.IntegerField(min_value=1, max_value=99)
    contact = forms.EmailField()

데이터 검증

폼 인스턴스에 데이터를 바인딩하면 is_valid() 메서드를 통해 유효성 검증을 수행할 수 있습니다.

form_instance = UserRegistrationForm({
    "nickname": "devuser",
    "level": 101,
    "contact": "user@example.com"
})

# 모든 데이터가 유효한지 확인
form_instance.is_valid()
# 유효한 데이터만 추출
form_instance.cleaned_data   # {'nickname': 'devuser', 'contact': 'user@example.com'}
# 오류 메시지 확인
form_instance.errors
  • is_valid() 메서드 내부에서 full_clean() 함수가 실행되며, 이를 통해 cleaned_data 속성이 생성됩니다.

HTML 태그 자동 생성

폼 인스턴스를 템플릿에 전달하여 다양한 방식으로 입력 태그를 렌더링할 수 있습니다.

완전 자동 생성:

<form action="" method="post">
    {{ form_instance.as_p }}
    <input type="submit">
</form>

위 코드는 세 개의 p 태그로 감싸진 입력 필드를 생성합니다. as_ulas_table도 지원합니다.

수동 커스터마이징:

<form action="" method="post">
    {% for field in form_instance %}
        {{ field.label_tag }}
        {{ field }}
    {% endfor %}
    <input type="submit">
</form>

각 필드에 CSS 클래스나 사용자 정의 속성을 추가하려면 widget 파라미터를 활용합니다.

nickname = forms.CharField(
    max_length=10,
    min_length=3,
    widget=forms.widgets.TextInput(attrs={'class': 'input-field', 'data-role': 'username'})
)

오류 메시지 출력

폼의 errors 속성을 템플릿에서 직접 사용할 수 있습니다.

<form action="" method="post" novalidate>
    {% for field in form_instance %}
    {{ field.label_tag }}
    {{ field }}
    {{ field.errors.0 }}
    {% endfor %}
    <input type="submit">
</form>

요청 처리 로직 예시

from django.shortcuts import render, HttpResponse

class UserRegistrationForm(forms.Form):
    nickname = forms.CharField(max_length=10, min_length=3)
    level = forms.IntegerField(min_value=1, max_value=99)
    contact = forms.EmailField()

def register_view(request):
    form = UserRegistrationForm()
    if request.method == "POST":
        form = UserRegistrationForm(request.POST)
        if form.is_valid():
            return HttpResponse('가입 완료')
    return render(request, "registration.html", {"form": form})
  • request.POST는 딕셔너리 형태로 폼에 직접 전달할 수 있습니다. 불필요한 데이터는 자동으로 무시됩니다.
  • novalidate 속성을 사용하면 브라우저의 기본 검증을 비활성화할 수 있습니다. 서버 측 검증이 더 안전합니다.
  • GET과 POST 요청 모두 동일한 form 변수명을 사용하여 템플릿 일관성을 유지합니다.

검증 방식 확장

필드 파라미터

기본적으로 Django가 제공하는 제약 조건을 필드에 직접 설정할 수 있습니다.

name = forms.CharField(max_length=10, min_length=3)

정규식 검증

from django.core.validators import RegexValidator

class ContactForm(forms.Form):
    phone = forms.CharField(
        validators=[
            RegexValidator(r"\d+", "숫자만 입력 가능합니다"),
            RegexValidator(r"^010-\d{4}-\d{4}$", "휴대폰 번호 형식이 아닙니다"),
        ]
    )

훅(Hook) 함수

로컬 훅: 단일 필드 검증

def clean_nickname(self):
    submitted_name = self.cleaned_data.get('nickname')
    blacklist = ['admin', 'root']
    if submitted_name in blacklist:
        self.add_error('nickname', "사용할 수 없는 닉네임입니다")
    return submitted_name

글로벌 훅: 여러 필드 간 관계 검증

def clean(self):
    pwd = self.cleaned_data.get('password')
    confirm_pwd = self.cleaned_data.get('confirm_password')
    if pwd != confirm_pwd:
        self.add_error('confirm_password', "비밀번호가 일치하지 않습니다")
    return self.cleaned_data

Form 필드 주요 파라미터

파라미터 설명 비고
min_length 문자열 최소 길이
max_length 문자열 최대 길이
min_value 숫자 최솟값
max_value 숫자 최댓값
label 필드 레이블 템플릿에서 {{ field.label_tag }}로 출력
error_messages 사용자 정의 오류 메시지
validators 정규식 검증기 목록
initial 입력 필드의 기본값
required 필수 입력 여부 (기본 True) False로 설정 시 선택 사항
widget HTML 태그 스타일 및 속성

error_messages 예시

nickname = forms.CharField(
    max_length=10,
    min_length=3,
    error_messages={
        'max_length': "닉네임이 너무 깁니다",
        'min_length': "닉네임이 너무 짧습니다",
    }
)

initial 예시

class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=4,
        label="사용자명",
        initial="guest"
    )
    password = forms.CharField(min_length=6, label="비밀번호")

widget 예시

username = forms.CharField(
    max_length=10,
    min_length=3,
    widget=forms.widgets.TextInput(attrs={'class': 'form-control', 'custom': 'value'})
)

주요 위젯 타입:

위젯 클래스 HTML 태그
PasswordInput() 비밀번호 입력 (input type="password")
RadioSelect() 라디오 버튼 (input type="radio")
Select() 단일 선택 드롭다운 (select)
SelectMultiple() 다중 선택 드롭다운 (select multiple)
CheckboxInput() 체크박스 (input type="checkbox")
CheckboxSelectMultiple() 다중 체크박스

ModelForm 컴포넌트

ModelForm을 사용하면 모델 정의를 기반으로 폼을 자동 생성할 수 있어 코드 중복을 줄일 수 있습니다.

from django import forms
from .models import Book

class BookForm(forms.ModelForm):
    class Meta:
        model = Book
        fields = "__all__"
        labels = {
            "title": "도서명",
            "price": "가격"
        }
        widgets = {
            "password": forms.widgets.PasswordInput(attrs={"class": "secure-input"}),
        }

Meta 클래스 속성

속성 설명
model 참조할 모델 클래스
fields 포함할 필드 ( "__all__" 또는 리스트)
exclude 제외할 필드
labels 레이블 재정의
help_texts 도움말 텍스트
widgets 위젯 커스터마이징
error_messages 오류 메시지 커스터마이징

ModelForm 검증

Form과 마찬가지로 is_valid() 또는 errors 접근 시 검증이 실행됩니다. 별도로 validators를 설정하지 않으면 모델 필드의 검증 규칙이 그대로 적용됩니다. 동일한 방식으로 로컬 및 글로벌 훅을 작성할 수 있습니다.

save() 메서드

ModelForm은 데이터베이스에 객체를 저장하는 save() 메서드를 제공합니다.

>>> from myapp.forms import BookForm

# 신규 객체 생성
>>> form = BookForm(request.POST)
>>> form.save()

# 기존 객체 업데이트
>>> existing_book = Book.objects.get(id=1)
>>> form = BookForm(request.POST, instance=existing_book)
>>> form.save()

태그: Django Form ModelForm python 웹개발

7월 3일 23:42에 게시됨