장고 모델 폼 (Model Form)
06 May 2017 | python Django formAskDjango 수업을 듣고 중요한 내용을 정리하였습니다.
Model Form
- form 클래스를 상속받은 클래스 (forms.ModelForm)
- form 을 만들 때 model 클래스의 내역 그대로 form을 만든다면, (Model Form)
forms.py 에서 form 필드를 중복해서 정의할 필요가 없다 - 모델과 관련된 form 이라면 모델 폼을 사용하는 것이 좋다
Form vs Model Form (폼과 모델폼의 차이점)
- Form (일반 폼) : 직접 필드 정의, 위젯 설정이 필요
- Model Form (모델 폼) : 모델과 필드를 지정하면 모델폼이 자동으로 폼 필드를 생성
from django import forms
from .models import Post
# Form (일반 폼)
class PostForm(forms.Form):
title = forms.CharField()
content = forms.CharField(widget=forms.Textarea)
# Model Form (모델 폼)
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content'] # '__all__' 설정시 전체 필드 추가
ModelForm.save(commit=True)
- Model Form 클래스에는 .save(self, commit=True) 메소드가 구현되 었음
- DB 저장 여부를 commit flag를 통해서 결정
- commit=False flag를 통해 함수 호출을 지연
# dojo/forms.py
from django import forms
from .models import Post
class PostForm(forms.ModelForm): # 모델 폼 정의
class Meta:
model = Post
fields = ['title', 'content']
''' 내부적으로 구현되어 있음 (멤버변수 인스턴스)
향후 수정 기능 구현시 활용
def save(self, commit=True):
self.instance = Post(**self.cleaned_data)
if commit:
self.instance.save()
return self.instance
'''
사용예시
- form.save(commit=False) 를 통해서 DB save 를 지연시켜 중복 save를 방지한다.
from django.shortcuts import render, redirect
from .models import Post
from .forms import PostForm
def post_new(request):
if request.method == 'POST':
form = PostForm(request.POST, request.FILES) # NOTE: 인자 순서주의 POST, FILES
if form.is_valid():
post = form.save(commit=False) # 중복 DB save를 방지
post.ip = request.META['REMOTE_ADDR'] # ip 필드는 유저로 부터 입력 받지 않고 프로그램으로 채워 넣는다
post.save()
return redirect('/dojo/')
else:
form = PostForm()
return render(request, 'dojo/post_form.html',{
'form': form,
})
ModelForm을 활용한 Model Instance 수정
- 수정 대상이 되는 model instance 를 Model Form 인스턴스 생성시에 instance 인자로서 지정한다
# myapp/views.py
def post_edit(request, id):
post = get_object_or_404(Post, id=id)
if request.method == 'POST':
form = PostModelForm(request.POST, request.FILES, instance=post) # NOTE: instance 인자(수정대상) 지정
if form.is_valid():
post = form.save()
return redirect(post) # NOTE: post 인스턴스의 get_absolute_url 메소드 호출!
else:
form = PostModelForm(instance=post)
return render(request, 'myapp/post_form.html',{
'form': form,
})
(중요) .cleaned_data 을 끝까지 활용할 것!
cleaned_data
- .is_valid() 를 통해서 검증에 통과한 값은
cleaned_data
변수명으로사전타입
으로 제공된다.
def post_new(request):
if request.method == 'POST':
form = PostForm(request.POST, request.FILES) # NOTE: 인자 순서주의 POST, FILES
if form.is_valid():
print(form.cleaned_data) # {'title': '테스트', 'content': '내용'}
# ... 생략 ...
request.POST[‘key’] 보다는 form.cleaned_data[‘key’] 와 같이 사용
- request.POST 데이터는 form instance의 초기 데이터
- 따라서 form clean 함수 등을 통해 변경될 가능성이 있음
# myapp/forms.py
class CommentForm(forms.Form):
def clean_message(self): # Form 클래스 내 clean 멤버함수를 통해 값 변경이 가능
return self.cleaned_data.get('message', '').strip() # 좌우 공백 제거
# myapp/views.py
# BAD Case!! - request.POST를 통한 접근
form = CommentForm(request.POST)
if form.is_valid():
# request.POST : 폼 인스턴스 초기 데이터
message = request.POST['message']
comment = Comment(message=message)
comment.save()
return redirect(post)
# GOOD Case!! - form.cleaned_data를 통한 접근
form = CommentForm(request.POST)
if form.is_valid():
# form.cleaned_data : 폼 인스턴스 내에서 clean 함수를 통해 변환되었을 수도 있을 데이터
message = form.cleaned_data['message']
comment = Comment(message=message)
comment.save()
return redirect(post)