기존에는 사용자의 데이터를 입력받기 위해 HTML의 <form> 태그를 직접 작성했다.
하지만 HTML Form 자체는 사용자가 입력한 데이터가 올바른 데이터인지 검사하는 기능이 부족하다.
예를 들어 다음과 같은 상황을 직접 처리해야 한다.
이러한 검증 로직을 직접 작성하면 코드가 복잡해질 수 있다.
사용자가 입력한 데이터가 정확하고 유효한지 확인하는 과정이다.
Django Form은 사용자가 입력한 데이터를 자동으로 검사하고, 잘못된 값이 입력되면 오류 메시지를 제공할 수 있다.
유효성 검사를 통해 잘못된 데이터가 데이터베이스에 저장되는 것을 막을 수 있다.
Django Form에서는 is_valid() 메서드를 사용해 입력 데이터의 유효성을 검사한다.
form.is_valid()
True 반환False 반환이후 실제 게시글 생성 과정에서는 is_valid()의 결과가 True인 경우에만 데이터를 저장하도록 처리한다.
사용자 입력 데이터를 수집하고 처리하며, 유효성 검사를 수행하기 위한 Django의 도구이다.
Django Form을 사용하면 다음 기능을 쉽게 구현할 수 있다.
Django Form을 사용하기 위해 forms.py 파일을 생성하고 Form 클래스를 작성한다.
from django import forms
class ArticleForm(forms.Form):
title = forms.CharField(max_length=10)
content = forms.CharField()
forms.Form을 상속받아 Form 클래스를 생성한다.
titlecontentviews.py에서 ArticleForm을 import하여 Form 객체를 생성한다.
from .forms import ArticleForm
def new(request):
form = ArticleForm()
context = {
"form": form,
}
return render(request, "articles/new.html", context)
ArticleForm()을 생성한 후 context를 통해 템플릿으로 전달한다.
기존에는 HTML input을 직접 작성했다.
<input type="text" name="title">
<textarea name="content"></textarea>
Django Form을 사용하면 다음과 같이 간단하게 작성할 수 있다.
<form action="{% url 'articles:create' %}" method="POST">
{% csrf_token %}
{{ form }}
<input type="submit">
</form>
{{ form }}을 작성하면 Django가 Form 클래스에 정의된 필드를 기반으로 HTML 입력 요소를 자동 생성한다.
적용 후에도 {{ form }} 을 통해 Django Form이 실제 HTML 입력 요소로 렌더링 된 것을 확인 할 수 있다.

Django Form의 각 입력 필드가 HTML에서 어떤 형태로 출력될지 결정하는 구성 요소이다.
예를 들어 기본 CharField는 HTML에서 <input> 형태로 출력된다.
하지만 긴 내용을 입력받아야 하는 경우 <textarea> 형태가 더 적절할 수 있다.
class ArticleForm(forms.Form):
# 사용자로부터 입력받을 필드를 정의
title = forms.CharField(max_length=10)
content = forms.CharField(widget=forms.Textarea)

forms.Textarea를 사용하면 content 입력 필드가 <textarea> 형태로 출력된다.
Widget을 사용하면 다음과 같은 HTML 입력 형태를 지정할 수 있다.
즉, Widget은 입력 필드의 화면 표현 방식을 결정한다.
Django Form과 ModelForm은 모두 사용자 입력 데이터를 처리하지만 목적이 다르다.
| 구분 | Form | ModelForm |
|---|---|---|
| Model 연결 | 연결되지 않음 | 연결됨 |
| 주요 목적 | 사용자 입력 처리 및 검증 | 사용자 입력 처리 + DB 저장 |
| 사용 예시 | 검색, 로그인 | 게시글 작성, 회원가입 |
| DB 저장 | 직접 처리 필요 | save() 사용 가능 |
Model과 연결된 Form을 자동으로 생성해주는 Django 클래스이다.
ModelForm은 Form과 Model을 결합한 형태이다.
Model에 정의된 필드를 기반으로 Form 필드를 자동으로 생성하고, 사용자 입력 데이터를 데이터베이스에 저장할 수 있다.
기존 forms.Form 대신 forms.ModelForm을 상속받는다.
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = "__all__"

Meta 클래스를 사용해도 Model의 필드를 기반으로 동일하게 Form이 생성되는 것을 확인할 수 있다.
ModelForm 내부에서 어떤 Model과 연결할지, 어떤 필드를 사용할지 설정하는 공간이다.
class Meta:
model = Article
fields = "__all__"
model = Articlefields = "__all__"class Meta:
model = Article
fields = ("title",)
class Meta:
model = Article
exclude = ("title",)
fields 또는 exclude를 사용하여 Form에 어떤 필드를 사용할지 결정할 수 있다.
기존에는 사용자 입력값을 직접 꺼내서 Article 객체를 생성했다.
title = request.POST.get("title")
content = request.POST.get("content")
article = Article(
title=title,
content=content,
)
article.save()
ModelForm을 사용하면 이 과정을 줄일 수 있다.
form = ArticleForm(request.POST)
사용자가 입력한 request.POST 데이터를 ArticleForm에 전달한다.
Form에 입력된 데이터의 유효성을 검사하고 결과를 True 또는 False로 반환하는 메서드이다.
if form.is_valid():
입력 데이터가 정상이라면: True 를 반환한다.
잘못된 데이터가 있다면: False 를 반환한다.
def create(request):
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save()
return redirect("articles:detail", article.pk)
context = {
"form": form,
}
return render(request, "articles/new.html", context)
동작 과정은 다음과 같다.
사용자 데이터 입력
↓
ArticleForm(request.POST)
↓
form.is_valid()
↓
정상 데이터 → True → DB 저장
잘못된 데이터 → False → 오류 메시지 출력
입력값이 잘못된 경우 Django Form 객체 내부에 오류 정보가 저장된다.
이 Form 객체를 다시 Template에 전달하면 사용자는 어떤 입력이 잘못되었는지 확인할 수 있다.

ModelForm의 데이터를 기반으로 Model 객체를 생성하거나 수정하고 데이터베이스에 저장하는 메서드이다.
article = form.save()
Form에 입력된 데이터가 유효하다면 save() 메서드를 사용하여 데이터베이스에 바로 저장할 수 있다.
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save()
새로운 객체를 생성하는 경우 별도의 instance를 전달하지 않는다.


article = Article.objects.get(pk=article_pk)
form = ArticleForm(
request.POST,
instance=article,
)
if form.is_valid():
form.save()
수정할 기존 객체를 instance에 전달하면 새로운 객체를 생성하지 않고 기존 객체를 수정한다.


instance없음 → 새로운 데이터 생성
instance있음 → 기존 데이터 수정
기존 게시글 작성 기능은 두 개의 View 함수로 나누어져 있었다.
new()
→ GET 요청
→ 게시글 작성 페이지 출력
create()
→ POST 요청
→ 게시글 저장
두 함수의 목적은 결국 게시글 생성이라는 동일한 목적을 가진다.
따라서 HTTP Request Method를 이용하여 하나의 View 함수로 합칠 수 있다.
GET /articles/new/
→ new()
POST /articles/create/
→ create()
GET /articles/create/
→ 작성 페이지 출력
POST /articles/create/
→ 게시글 생성
하나의 URL과 View 함수에서 요청 방식에 따라 다른 작업을 수행한다.
def create(request):
if request.method == "POST":
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save()
return redirect("articles:detail", article.pk)
else:
form = ArticleForm()
context = {
"form": form,
}
return render(request, "articles/create.html", context)
else:
form = ArticleForm()
GET 요청이 들어오면 빈 Form을 생성하여 작성 페이지를 보여준다.
if request.method == "POST":
form = ArticleForm(request.POST)
POST 요청이 들어오면 사용자가 작성한 데이터를 Form에 담는다.
이후:
if form.is_valid():
유효성 검사를 수행한다.
정상 데이터라면:
article = form.save()
데이터베이스에 저장한다.
기존의 new/ URL은 더 이상 필요하지 않기 때문에 제거한다.
urlpatterns = [
path("", views.index, name="index"),
path("<int:article_pk>/", views.detail, name="detail"),
path("create/", views.create, name="create"),
]
게시글 수정 역시 기존에는 두 개의 View 함수로 나누어져 있었다.
edit()
→ 수정 페이지 출력
update()
→ 수정된 데이터 저장
이 두 함수 역시 게시글 수정이라는 같은 목적을 가지고 있으므로 하나의 함수로 결합할 수 있다.
def update(request, article_pk):
article = Article.objects.get(pk=article_pk)
if request.method == "POST":
form = ArticleForm(
request.POST,
instance=article,
)
if form.is_valid():
form.save()
return redirect("articles:detail", article.pk)
else:
form = ArticleForm(instance=article)
context = {
"article": article,
"form": form,
}
return render(request, "articles/update.html", context)
form = ArticleForm(instance=article)
기존 게시글 데이터를 Form에 넣어 수정 페이지에 보여준다.
form = ArticleForm(
request.POST,
instance=article,
)
기존 게시글 객체와 사용자가 수정한 데이터를 함께 전달한다.
instance=article을 작성하지 않으면 새로운 게시글이 생성될 수 있다.
따라서 수정에서는 반드시 수정할 기존 객체를 전달해야 한다.
ModelForm은 여러 개의 인자를 받을 수 있다.
ArticleForm(request.POST, instance=article)
여기서 request.POST는 첫 번째 인자인 data에 전달된다.
instance=article은 수정할 기존 Model 객체를 전달한다.
instance는 여러 인자 중 뒤쪽에 위치하기 때문에 일반적으로 이름을 명시하여 전달한다.
instance=article
Widget을 사용하면 입력 필드의 속성을 세부적으로 설정할 수 있다.
class ArticleForm(forms.ModelForm):
title = forms.CharField(
label="제목",
widget=forms.TextInput(
attrs={
"class": "my-title",
"placeholder": "Enter the title",
}
),
)
class Meta:
model = Article
fields = "__all__"
attrs를 사용하면 HTML의 속성을 지정할 수 있다.
classplaceholderrowscols등을 추가할 수 있다.
content = forms.CharField(
label="내용",
widget=forms.Textarea(
attrs={
"class": "my-content",
"placeholder": "Enter the content",
"rows": 5,
"cols": 50,
}
),
)

{{ form }}을 사용하면 전체 Form을 한 번에 출력할 수 있다.
하지만 각 필드를 직접 배치하고 싶은 경우 개별적으로 렌더링할 수도 있다.
<form action="" method="POST">
{% csrf_token %}
<div>
{{ form.title.errors }}
<label for="{{ form.title.id_for_label }}">
Title:
</label>
{{ form.title }}
</div>
<div>
{{ form.content.errors }}
<label for="{{ form.content.id_for_label }}">
Content:
</label>
{{ form.content }}
</div>
<input type="submit">
</form>
각 필드를 직접 출력하면 HTML 구조와 디자인을 더 세밀하게 조절할 수 있다.