모델 데이터가 중요 --> URL --> VIEW --> TRMPLETE
VIEW는 많은역할을 함 그래서 1편에서 2편으로 넘어갈때 코드가 동일한지 확인체크
ModelForm을 만들고, 함수형 View 기준으로 공부기록 생성(Create), 상세(Detail), 수정(Update), 삭제(Delete), 페이지네이션까지 완성하며 브라우저 기반 CRUD 흐름을 자세히 익힙니다.



# models.py의 필드를 자동으로 HTML 폼으로 변환
class PostForm(ModelForm):
class Meta:
model = Post
fields = ['title', 'content']
DB 모델을 기반으로 입력 폼을 자동 생성해주는 역할
질문
models.py의 필드를 자동으로 HTML 폼으로 변환 class PostForm(ModelForm): class Meta: model = Post fields = ['title', 'content'] > DB 모델을 기반으로 입력 폼을 자동 생성해주는 역할 모델폼은 장고내장 함수인건가?
답변
Django 내장 클래스입니다
어디서 가져오나요?
# Django 내장 모듈에서 import
from django import forms
class PostForm(forms.ModelForm): # Django가 제공하는 클래스 상속
class Meta:
model = Post
fields = ['title', 'content']
이제 본격적으로 폼을 만들기 전에, 왜 Django에서 Form 또는 ModelForm을 쓰는지 먼저 이해하고 갑니다.
초보자는 자주 이렇게 생각합니다.
HTML form 만들고 request.POST에서 값 꺼내서 저장하면 되는 거 아닌가요?
물론 기술적으로는 가능합니다. 하지만 Django는 Form을 쓰는 방식이 훨씬 체계적이고 안전합니다.
Form / ModelForm을 쓰는 이유
입력값 검증이 쉬움
에러 메시지 처리 쉬움
필드 관리가 쉬움
템플릿 렌더링이 쉬움
코드 구조가 Django 표준 흐름에 맞음
특히 ModelForm은 모델과 직접 연결되어 있어서 아주 강력합니다.
즉 아래 흐름이 가능해집니다.
Study 모델 정의
→ ModelForm 작성
→ 폼에서 입력값 검증
→ form.save()로 모델 저장
즉 ModelForm은 모델 기반 폼 자동화 도구라고 이해하면 좋습니다.
| GET | POST | |
|---|---|---|
| 용도 | 데이터 조회 | 데이터 전송/저장 |
| 데이터 위치 | URL에 노출 | Body에 숨겨짐 |
| 보안 | 낮음 | 높음 |
| 데이터 크기 | 제한 있음 | 제한 없음 |
| 캐싱 | 가능 | 불가능 |
# GET - URL에 데이터가 보임
https://example.com/search?keyword=python&page=1
# POST - URL에 데이터가 안 보임
https://example.com/login
(아이디/비밀번호는 숨겨져서 전송)
def my_view(request):
# GET 요청 - 데이터 조회할 때
if request.method == 'GET':
posts = Post.objects.all()
return render(request, 'list.html', {'posts': posts})
# POST 요청 - 데이터 저장할 때
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.save() # DB 저장
질문 GET POST
GET → 검색, 목록 조회, 상세 보기
POST → 로그인, 회원가입, 게시글 작성, 수정, 삭제
HTML 폼에서
<!-- GET 방식 -->
<form method="GET">
<input type="text" name="keyword">
</form>
<!-- POST 방식 -->
<form method="POST">
{% csrf_token %} <!-- Django 보안 토큰 필수! -->
<input type="text" name="title">
</form>
💡 조회는 GET, 변경은 POST 로 기억하세요!
질문 장고에서의 모델 폼에대한 설명과 사용쓰임방법
DB 모델(Model) + HTML 폼(Form) 을 자동으로 연결해주는 Django 내장 클래스
기본 구조
# forms.py
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post # 어떤 모델을 사용할지
fields = ['title', 'content'] # 어떤 필드를 폼에 보여줄지
# fields = '__all__' # 모든 필드 사용할 때
Model 예시
# models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100) # 제목
content = models.TextField() # 내용
created_at = models.DateTimeField(auto_now_add=True) # 생성일
CRUD별 사용 방법
✅ 생성 (Create)
# views.py
def post_create(request):
if request.method == 'POST':
form = PostForm(request.POST) # 입력된 데이터 받기
if form.is_valid(): # 유효성 검사
form.save() # DB 저장
return redirect('post_list')
else:
form = PostForm() # 빈 폼 생성
return render(request, 'create.html', {'form': form})
✅ 수정 (Update)
# views.py
def post_update(request, pk):
post = Post.objects.get(pk=pk)
if request.method == 'POST':
form = PostForm(request.POST, instance=post) # 기존 데이터 불러오기
if form.is_valid():
form.save() # 수정된 내용 저장
return redirect('post_list')
else:
form = PostForm(instance=post) # 기존 데이터로 폼 채우기
return render(request, 'update.html', {'form': form})
✅ 삭제 (Delete)
# views.py
def post_delete(request, pk):
post = Post.objects.get(pk=pk)
if request.method == 'POST':
post.delete() # DB에서 삭제
return redirect('post_list')
return render(request, 'delete.html', {'post': post})
HTML 템플릿에서 사용
<!-- create.html -->
<form method="POST">
{% csrf_token %} <!-- 보안 토큰 필수! -->
{{ form.as_p }} <!-- 폼 자동 렌더링 -->
<button type="submit">저장</button>
</form>
폼 렌더링 방식 3가지
{{ form.as_p }} <!-- <p> 태그로 감싸서 출력 -->
{{ form.as_table }} <!-- <table> 태그로 출력 -->
{{ form.as_ul }} <!-- <ul> 태그로 출력 -->
유효성 검사
form = PostForm(request.POST)
if form.is_valid(): # 유효성 통과 ✅
form.save()
else:
print(form.errors) # 어떤 오류인지 출력 ❌
fields 설정 방법
class Meta:
model = Post
fields = '__all__' # 모든 필드
fields = ['title', 'content'] # 특정 필드만 포함
exclude = ['created_at'] # 특정 필드만 제외
일반 Form vs ModelForm 비교
# ❌ 일반 Form - 직접 다 작성
class PostForm(forms.Form):
title = forms.CharField()
content = forms.CharField()
# DB 저장도 직접 작성해야 함
# ✅ ModelForm - 자동 처리
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = '__all__'
# DB 저장은 form.save() 한 줄!
핵심 메서드 정리
| 메서드 | 설명 |
|---|---|
form.is_valid() | 입력값 유효성 검사 |
form.save() | DB에 저장 |
form.errors | 오류 내용 확인 |
form.as_p() | p태그로 렌더링 |
instance=post | 기존 데이터 불러오기 |
💡 ModelForm = 모델 기반 자동 폼 생성 + 유효성 검사 + DB 저장 을 한번에 처리해주는 Django의 핵심 기능입니다!
질문 Django Form / ModelForm은 왜 필요하며 둘 차이점?
Django Form vs ModelForm
왜 필요한가?
HTML 폼만 사용하면 아래를 전부 직접 작성해야 함 😱
1. HTML input 태그 하나하나 작성
2. 유효성 검사 직접 작성
3. DB 저장 코드 직접 작성
4. 보안 처리 직접 작성
→ Form / ModelForm 사용하면 자동으로 해결! 😊
Django Form
# forms.py
from django import forms
class PostForm(forms.Form):
title = forms.CharField(max_length=100) # 직접 필드 정의
content = forms.CharField() # 직접 필드 정의
# views.py
def post_create(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
# ❌ DB 저장 코드를 직접 작성해야 함
Post.objects.create(
title=form.cleaned_data['title'],
content=form.cleaned_data['content']
)
else:
form = PostForm()
return render(request, 'create.html', {'form': form})
Django ModelForm
# forms.py
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post # 모델 연결
fields = '__all__' # 필드 자동 생성
# views.py
def post_create(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.save() # ✅ 한 줄로 DB 저장
else:
form = PostForm()
return render(request, 'create.html', {'form': form})
핵심 차이점
| Form | ModelForm | |
|---|---|---|
| 필드 정의 | 직접 작성 | 모델에서 자동 생성 |
| DB 저장 | 직접 작성 | form.save() 한 줄 |
| 모델 연결 | ❌ 없음 | ✅ 자동 연결 |
| 코드량 | 많음 | 적음 |
| 유효성 검사 | ✅ 가능 | ✅ 가능 |
| 사용 상황 | DB 불필요한 폼 | DB 연동 필요한 폼 |
언제 무엇을 쓰나요?
✅ Form 사용
- 로그인 (DB 저장 없이 검증만)
- 검색 폼
- 이메일 문의 폼
- DB와 관계없는 데이터 처리
✅ ModelForm 사용
- 게시글 작성/수정
- 회원가입
- 댓글 작성
- DB에 저장이 필요한 모든 것
한눈에 비교
# Form - 검색 폼 예시 (DB 저장 X)
class SearchForm(forms.Form):
keyword = forms.CharField()
# ModelForm - 게시글 폼 (DB 저장 O)
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = '__all__'
💡 DB 저장이 필요하면 ModelForm, 아니면 Form!
ModelForm은 Form의 기능을 모두 포함하면서 DB 연동까지 자동으로 해줍니다.
1편 순서기억 이해
Form / ModelForm을 쓰는 이유
나의질문 폼이랑 모델폼차이가 이해가 안되신다는건가용??
Study 모델 정의
→ ModelForm 작성
→ 폼에서 입력값 검증
→ form.save()로 모델 저장
답변
스터디 모델을 1편에서 만들었잖아요?
그 모델이랑 연결되는 form을 만들라는거에용
모델과 연관성있는 것 무언가 입력을 받는 구조의 폼이라고 생각
Form은
그냥 입력창을 만드는 도구
어떤 값을 받을지 직접 다 적어줘야 함
저장도 직접 해야 함
즉, “입력만 받는 일반 양식”이라고 이해하시면 됩니다.
ModelForm은
모델을 기준으로 폼을 쉽게 만드는 도구
모델 필드를 바탕으로 입력창을 만들 수 있음
검증도 도와주고, form.save()로 DB 저장도 쉬움
즉, “모델이랑 연결된 폼”이라고 이해하시면 됩니다.
우리가 템플릿에서 보는
태그는
사용자가 값을 입력해서 보내는 HTML 영역이에요!
사용자가 입력한 값이 서버로 전달되면,
서버에서 ModelForm이 그 값을 받아 검증하고,
문제가 없으면 저장한다고 생각하시면 됩니당
나의질문
study 앱에 폼을 만들고 그 폼이라는것은 그냥 앱 생성할때마다 만드는 폼이라고 생각하면되나요.. 그리고 그안에 CRUS페이지까지 스터디앱에 만드는거죠?
답변
폼은 앱 만들 때 무조건 만드는 게 아니라, 사용자의 입력을 받아야 할 때 만드는 거예요! 항상 만드는건 아니에용 그리고 Study 앱을 만들었으니까, Study 관련 CRUD 페이지도 그 앱 안에서 같이 만듭니다
Form vs ModelForm 적용 범위
아니요, 둘 다 모델 파일과 독립적으로 사용할 수 있습니다.
forms.Form — 모델과 무관
class ContactForm(forms.Form):
name = forms.CharField()
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
모델 없이 순수하게 입력값 처리/검증만 할 때 사용
예: 문의 폼, 검색 폼, 로그인 폼, 계산기 폼 등
forms.ModelForm — 모델과 연결됨
class StudyForm(forms.ModelForm):
class Meta:
model = Study # ← 특정 모델에 종속
fields = [...]
반드시 Meta.model에 연결할 모델이 필요
form.save()로 DB에 바로 저장 가능
정리
구분 모델 필요 여부 DB 저장
forms.Form ❌ 불필요 직접 처리해야 함
forms.ModelForm ✅ 필수 form.save()로 자동
즉, ModelForm만 모델 파일에 해당되고, Form은 모델 없이 어디서든 독립적으로 사용할 수 있습니다.
예시 1 - 검색 폼
class SearchForm(forms.Form):
keyword = forms.CharField()
# 검색어를 받아서 필터링만 할 뿐, DB에 저장 안 함
예시 2 - 로그인 폼
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
# 인증만 하고 저장 X
예시 3 - 문의/이메일 폼
class ContactForm(forms.Form):
email = forms.EmailField()
message = forms.CharField()
# 이메일로 전송만 하고 저장 X
용어 설명 예시
클래스(Class) 설계도 전체 StudyForm, LoginForm
메서드(Method) 클래스 안의 함수 clean_minutes(), save()
필드(Field) 클래스 안의 변수/속성 title, email
class StudyForm(forms.ModelForm): # ← 클래스
title = forms.CharField() # ← 필드
def clean_minutes(self): # ← 메서드
...
그래서
StudyForm 자체 → 클래스
clean_minutes() → 메서드
forms.Form, forms.ModelForm → 클래스를 상속받는 것
"Form이랑 ModelForm 메서드야?" 라고 하면 엄밀히는 클래스라고 표현하는 게 맞습니다.
이제 실제로 Study 모델과 연결되는 StudyForm을 만듭니다.
ModelForm이 자동으로 해주는 것
| 기능 | 설명 |
|---|---|
| 필드 자동 생성 | 모델의 필드를 자동으로 HTML <input>으로 변환 |
| 유효성 검사 | 입력값이 올바른지 자동 검증 |
| DB 저장 | .save() 한 줄로 DB에 바로 저장 |
직접 만들 때 vs ModelForm 비교
# ❌ 직접 만들 때 (길고 복잡)
<input type="text" name="title">
<input type="text" name="content">
# 유효성 검사도 직접 작성...
# DB 저장도 직접 작성...
# ✅ ModelForm 사용 (간단!)
form = PostForm() # 끝!
form.save() # DB 저장도 한 줄!
결론
Django 내장 클래스 ✅
별도 설치 불필요 ✅
from django import forms 로 바로 사용 가능 ✅
💡 Django가 반복적인 폼 작업을 자동화해주기 위해 만든 강력한 내장 기능입니다!
사용자가 폼에 데이터 입력 → DB에 새 데이터 저장
예) 게시글 작성, 회원가입
새로운 데이터를 추가하는 기능
목록에서 항목 클릭 → 해당 데이터 상세 내용 표시
예) 게시글 본문 보기
특정 데이터 하나를 자세히 조회하는 기능
기존 데이터를 불러와 폼에 표시 → 수정 후 저장
예) 게시글 편집
기존 데이터를 변경하는 기능
특정 데이터 선택 → DB에서 완전 삭제
예) 게시글 삭제
데이터를 DB에서 제거하는 기능
데이터가 많을 때 → 한 페이지에 n개씩 나눠서 표시
예) 1페이지, 2페이지, 3페이지...
많은 데이터를 나눠서 보여주는 기능
ModelForm → 입력 폼 자동 생성
Create (생성) → 데이터 추가 (INSERT)
Detail (상세) → 데이터 1개 조회 (SELECT)
Update (수정) → 데이터 변경 (UPDATE)
Delete (삭제) → 데이터 제거 (DELETE)
Pagination → 데이터 분할 표시
💡 이 6가지가 합쳐지면 브라우저 기반 CRUD 완성입니다!


이 순서는 기능을 그냥 나열한 것이 아닙니다.
예를 들어:
폼이 있어야 생성/수정 기능을 만들 수 있습니다.
상세 페이지가 있어야 수정/삭제 이후 이동 흐름도 자연스러워집니다.
CRUD가 어느 정도 끝나야 페이지네이션을 실제로 체감할 수 있습니다.
즉 이번 교안도 여전히 실제 실습 순서 기준으로 설계되어 있습니다.
2번 하기전 준비


path() 구조 분석
pythonpath('', views.study_list, name='list')
path(경로, 뷰함수, name=이름) — 총 3개의 인자로 구성됩니다.
form.save() 한 줄로 저장되는 이유쉽게 비유하면:
일반 Form = 그냥 빈 종이
ModelForm = Study 모델과 연결된 자동완성 종이
# ModelForm은 이미 내부에 이런 동작이 내장되어 있음
class StudyForm(forms.ModelForm):
class Meta:
model = Study # ← "나는 Study랑 연결됐어"
fields = ['title', ...] # ← "이 필드들을 저장해"
# 그래서 form.save() 한 줄이
# 내부적으로 이걸 자동으로 해줌
study = Study(
title = form.cleaned_data['title'],
category = form.cleaned_data['category'],
...
)
study.save() # DB 저장
form.save()= "연결된 모델에 알아서 넣고 저장해줘" 라는 명령
from django.shortcuts import redirect, render # ← 임포트
from .forms import StudyForm # ← 임포트
from .models import Study # ← 임포트
import = 다른 파일에서 필요한 것만 가져오는 것
from 어디서 import 무엇을def study_list(request): # ← 함수
...
return render(...)
def study_create(request): # ← 함수
...
return render(...)
def = 함수입니다 (메서드 아님)
| 구분 | 위치 | 예시 |
|---|---|---|
| 함수 | 클래스 밖에 독립적으로 존재 | def study_list(request) |
| 메서드 | 클래스 안에 존재 | def clean_minutes(self) |
def study_create(request):
form = StudyForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
form.save()
return redirect('study:list') # ← if 안의 return
return render(request, 'study/study_form.html', {...}) # ← 함수의 return
return이 2개인 이유
사용자가 처음 페이지 접속 (GET 요청)
→ if 조건 False → 아래 render() 실행 → 폼 화면 보여줌
사용자가 폼 작성 후 제출 (POST 요청)
→ if 조건 True → form.save() → redirect로 목록으로 이동
# 1. 임포트 (재료 가져오기)
from django.shortcuts import redirect, render
# 2. 함수 정의 (클래스 밖 → 함수)
def study_create(request):
# 3. 변수 선언
form = StudyForm(request.POST or None)
# 4. 조건 분기
if request.method == 'POST' and form.is_valid():
form.save()
return redirect('study:list') # POST면 여기서 끝
return render(...) # GET이면 여기서 끝
핵심:
def가 클래스 밖에 있으면 → 함수, 클래스 안에 있으면 → 메서드
어려움
이제 study_create View를 추가합니다.
{% extends 'base.html' %}
base.html을 부모로 삼음{% block title %}
{{ page_title }} | 공부시간 기록기
{% endblock %}
{% block content %}
... 실제 내용 ...
{% endblock %}
block = base.html에 뚫어놓은 구멍title → 브라우저 탭 제목content → 실제 화면에 보이는 내용 전체{% block content %}
│
├── section.hero-card ← 상단 소개 영역
│ 제목, 설명글
│
└── section.content-card ← 실제 폼 영역
form
hero-card (소개 영역)
<section class="hero-card">
<span>Study Form</span> <!-- 작은 라벨 -->
<h1>{{ page_title }}</h1> <!-- 큰 제목 (views.py에서 전달) -->
<p>오늘의 공부를 구체적으로...</p> <!-- 설명글 -->
</section>
content-card (폼 영역)
<section class="content-card">
<form method="post">
{% csrf_token %} <!-- 보안 토큰 -->
공부 제목
카테고리
공부 날짜
시간 / 분 ← 이 둘은 나란히 배치 (inline-2)
메모
저장하기 버튼 / 목록으로 버튼
</form>
</section>
모든 필드가 똑같은 패턴으로 반복됩니다
<div class="form-row">
<!-- 라벨 -->
<label for="{{ form.title.id_for_label }}">
{{ form.title.label }}
</label>
<!-- 입력창 -->
{{ form.title }}
<!-- 에러 메시지 (있을 때만 표시) -->
{% if form.title.errors %}
{{ form.title.errors }}
{% endif %}
</div>
| 부분 | 역할 |
|---|---|
label | 입력창 설명 텍스트 |
{{ form.title }} | 실제 input 태그 렌더링 |
form.title.errors | 검증 실패시 에러 출력 |
① 시간/분 나란히 배치
<div class="form-row inline-2">
<div> 시간 입력 </div>
<div> 분 입력 </div>
</div>
inline-2 클래스로 두 개를 가로로 나란히 배치② 보안 토큰
<form method="post">
{% csrf_token %}
base.html 레이아웃 상속
→ 소개 영역 (hero-card)
→ 폼 영역 (content-card)
→ 필드마다 라벨 + 입력창 + 에러메시지 패턴 반복
→ 저장 or 목록으로 버튼