끄적이는 개발노트
260624_강의 정리 (Django 정리 - Form, View, Model) 본문
728x90
Web Framework · Django (2)
Django 정리 (2) — Form, View, Model의 3위 일체
- Model · View · Form은 DB 연동 여부와 부모-자식 관계 표현 여부로 얽힌 3위 일체 구조
- General View 6종으로 반복되는 CRUD를, ModelForm으로 Form-Model 연동을, FormSet으로 다중 입력을 처리
- 폼 검증 흐름(is_bound → clean → is_valid)과 URL 역참조까지 한 번에 정리
1. Form-View-Model, 왜 3위 일체인가
"왜 Form은 Model이나 View 없이는 제대로 동작하지 않는가" — Django의 핵심 삼각 구조
- Django에서 Model · View · Form은 서로 강하게 연결된 삼각 구조
- Model 없이는 Form이 어떤 데이터를 받을지 모름
- View 없이는 Form이 화면에 그려지거나 제출을 처리할 방법이 없음
- 연결을 가르는 두 가지 기준 → DB 모델과의 연동 여부, 부모-자식(ForeignKey) 관계 표현 여부
💡 새 개념
3위 일체 Model–View–Form
- Django 실무에서 가장 자주 함께 다뤄지는 세 요소
회원가입 기능 하나를 만들 때도 Model(User 정보 구조), Form(입력 검증), View(요청 처리)가 동시에 필요함
2. 전체 데이터 흐름
"DB에 저장된 데이터가 어떻게 화면까지 전달되는가" — Model에서 Template까지
Models (Class)
→
Objects (Manager)
→
QuerySet (쿼리 가능)
→
View (Contexts)
→
Template (Form class)
💡 새 개념
Manager Manager
- 모델 클래스에 기본 제공되는 객체, .objects로 접근
Student.objects 가 바로 Manager 인스턴스
QuerySet QuerySet
- Manager를 통해 만들어지는 "쿼리 가능한" 데이터 묶음, 아직 실행되지 않은 지연 평가(lazy) 객체
Student.objects.filter(kor__gte=80) 의 반환값이 QuerySet
핵심 요약
· request가 View에 들어오면 View는 Model에서 QuerySet을 받아 Context(딕셔너리)로 가공해 Template에 전달
· Admin도 같은 Model을 바탕으로 동작 — register()로 모델을 등록하면 관리자 화면이 자동 생성됨
· request가 View에 들어오면 View는 Model에서 QuerySet을 받아 Context(딕셔너리)로 가공해 Template에 전달
· Admin도 같은 Model을 바탕으로 동작 — register()로 모델을 등록하면 관리자 화면이 자동 생성됨
3. General View (Class-Based Generic View)
"반복되는 CRUD 로직을 매번 직접 작성해야 할까" — Django가 미리 만들어둔 6종 뷰
| View | 대응 동작 | 주로 쓰는 곳 |
|---|---|---|
| ListView | 목록 조회 (GET) | 게시글 리스트 |
| DetailView | 단일 항목 조회 (GET) | 게시글 상세 |
| CreateView | 생성 (GET: 빈 폼 / POST: 저장) | 글쓰기 |
| UpdateView | 수정 (GET: 기존값 채운 폼 / POST: 저장) | 글 수정 |
| DeleteView | 삭제 (GET: 확인 화면 / POST: 삭제) | 글 삭제 |
| FormView | Model과 무관한 폼 처리 | 문의하기, 검색 폼 |
주의
· ListView, DetailView, CreateView, UpdateView, DeleteView는 기본적으로 Model과 연동되어 있음 (model = MyModel 속성만 지정하면 동작)
· FormView는 Model 연동이 필수가 아님 — DB 저장 없이 단순 입력 처리에 적합
· ListView, DetailView, CreateView, UpdateView, DeleteView는 기본적으로 Model과 연동되어 있음 (model = MyModel 속성만 지정하면 동작)
· FormView는 Model 연동이 필수가 아님 — DB 저장 없이 단순 입력 처리에 적합
4. Form vs ModelForm
"입력값만 검증할 건지, DB 모델과 바로 연결할 건지"
| 구분 | forms.Form | forms.ModelForm |
|---|---|---|
| DB 모델 연동 | 직접 연동 없음, 필드 직접 정의 | Meta.model로 모델과 직접 연결 |
| 저장 방법 | View에서 직접 처리 필요 | form.save()로 즉시 DB 저장 가능 |
| 적합한 상황 | 검색·로그인·문의 폼처럼 DB 저장이 불필요한 입력 | 게시글 작성처럼 모델 데이터를 그대로 입력받는 경우 |
Form vs ModelForm
# Forms.Form class ContactForm(forms.Form): email = forms.EmailField() message = forms.CharField(widget=forms.Textarea) # Forms.ModelForm class PostForm(forms.ModelForm): class Meta: model = Post fields = ['title', 'content']
5. 폼의 상태 확인과 검증
"제출된 폼이 유효한지 어떻게 판단하는가"
request
→
GET : 빈 폼
/
POST : 속이 찬 폼 (bound form)
💡 새 개념
Form.is_bound is_bound
- 폼에 데이터가 채워졌는지 여부 (데이터 존재 여부와 유효성은 별개)
GET 요청으로 받은 빈 폼은 is_bound = False, POST로 제출된 폼은 is_bound = True
Form.clean() clean
- 폼 전체 레벨의 검증 로직, 여러 필드를 함께 검사할 때 사용
비밀번호와 비밀번호 확인 필드가 일치하는지 검사하는 로직이 여기 들어감
Form.is_valid() is_valid
- clean()을 포함한 모든 검증을 실행하고 통과 여부를 True/False로 반환
보통 View에서 if form.is_valid(): 형태로 사용
clean_필드명 clean_<field>
- 특정 필드 하나만 따로 검증하고 싶을 때 사용하는 네이밍 규칙
clean_email(self) 메서드를 정의하면 email 필드만 따로 검증 가능
GET vs POST
· GET 요청 → 빈 폼(또는 기존 값이 채워진 초기 폼) 반환
· POST 요청 → 제출된 데이터가 담긴 폼(bound form)을 검증한 뒤 저장 또는 에러 반환
· GET 요청 → 빈 폼(또는 기존 값이 채워진 초기 폼) 반환
· POST 요청 → 제출된 데이터가 담긴 폼(bound form)을 검증한 뒤 저장 또는 에러 반환
6. FormSet — 여러 개의 폼을 한 번에
"폼 하나가 아니라 여러 개를 동시에 다뤄야 할 때는"
| 종류 | 설명 | 사용 예시 |
|---|---|---|
| FormSet | 동일한 Form을 여러 개 묶어서 처리 | 여러 개의 연락처 입력 |
| ModelFormSet | ModelForm을 여러 개 묶어서 처리, DB와 직접 연동 | 여러 상품을 한 번에 등록 |
| InlineFormSet | ForeignKey로 연결된 부모-자식 모델을 한 화면에서 함께 처리 | 주문(부모) 안에 여러 주문 항목(자식) 입력 |
선택 기준
· 핵심 차이는 DB 모델과의 연동 여부(FormSet vs ModelFormSet)와 부모-자식 관계 표현 여부(InlineFormSet)
· InlineFormSet은 inlineformset_factory()로 생성하며, 게시글-댓글처럼 1:N 관계를 한 화면에서 다룰 때 유용
· 핵심 차이는 DB 모델과의 연동 여부(FormSet vs ModelFormSet)와 부모-자식 관계 표현 여부(InlineFormSet)
· InlineFormSet은 inlineformset_factory()로 생성하며, 게시글-댓글처럼 1:N 관계를 한 화면에서 다룰 때 유용
7. Form을 HTML로 렌더링하기
"Form 객체를 화면에 어떻게 보여줄 것인가"
| 태그 | 렌더링 형태 |
|---|---|
| {{ form.as_table }} | <tr> 행으로 구성, <table> 태그로 감싸야 함 |
| {{ form.as_p }} | 각 필드를 <p> 태그로 감쌈 |
| {{ form.as_ul }} | 각 필드를 <li> 태그로 감쌈, <ul>로 감싸야 함 |
| {{ form.as_div }} | Django 4.1+ 추가, <div> 기반 — 커스텀 스타일링에 가장 유연 |
참고
· 위 4종보다 실무에서는 필드를 하나씩 직접 꺼내 커스텀 마크업을 짜는 경우가 더 많음
· 예: {{ form.email.label }}, {{ form.email }}
· 위 4종보다 실무에서는 필드를 하나씩 직접 꺼내 커스텀 마크업을 짜는 경우가 더 많음
· 예: {{ form.email.label }}, {{ form.email }}
8. URL 매개변수와 이름 기반 역참조
"URL에 변수를 어떻게 담고, 코드에서는 그 URL을 어떻게 다시 찾아가는가"
URL 매개변수 처리 — class / function
# urls.py — path converter로 변수 전달 urlpatterns = [ path('post/<int:pk>/', PostDetailView.as_view(), name='post_detail'), ] # FBV에서 받기 def post_detail(request, pk): post = get_object_or_404(Post, pk=pk) ... # CBV에서 받기 — self.kwargs로 접근 class PostDetailView(DetailView): model = Post def get_object(self): return get_object_or_404(Post, pk=self.kwargs['pk'])
💡 새 개념
name → URL 역참조 reverse lookup
- urls.py에서 지정한 name 값으로, 실제 경로를 몰라도 URL을 찾아갈 수 있는 방법
Template에서는 {% url 'post_detail' pk=post.pk %}, View/코드에서는 reverse('post_detail', kwargs={'pk': post.pk})
요약
· URL을 하드코딩하지 않고 name으로 참조하면, 나중에 경로(path())가 바뀌어도 코드 수정이 필요 없음
· URL을 하드코딩하지 않고 name으로 참조하면, 나중에 경로(path())가 바뀌어도 코드 수정이 필요 없음
전체 요약
· Model–View–Form은 서로 떼어 생각할 수 없는 3위 일체 구조
· General View는 반복되는 CRUD를, ModelForm은 모델 연동을, FormSet은 다중 입력을 각각 줄여주는 역할
· URL의 name 기반 역참조는 유지보수성을 위한 Django의 핵심 관례
· Model–View–Form은 서로 떼어 생각할 수 없는 3위 일체 구조
· General View는 반복되는 CRUD를, ModelForm은 모델 연동을, FormSet은 다중 입력을 각각 줄여주는 역할
· URL의 name 기반 역참조는 유지보수성을 위한 Django의 핵심 관례
728x90
'Python' 카테고리의 다른 글
| 260623_강의 정리 (Django 구조) (0) | 2026.06.24 |
|---|---|
| 260527_강의 정리 (Scipy, 모델) (1) | 2026.05.27 |
| 260526_강의 정리(Scipy, 수치해석) (0) | 2026.05.27 |
| 260522_강의 정리 (시계열분석, Statsmodel) (0) | 2026.05.27 |
| 260521_강의 정리 (시계열 분석) (0) | 2026.05.21 |