장고 모델 폼 (Model Form)

|

AskDjango 수업을 듣고 중요한 내용을 정리하였습니다.


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)