ModelForm // django Form(3)

2022. 4. 17. 17:03
  • 인프런의 이진석 강사님의 장고강의를 참고

ModelForm

  • 장고 Form을 상속받음
  • 지정된 Model로부터 필드정보를 읽어들여, Form Fields를 세팅할 수 있다.
  • 내부적으로 Model instance 생성하여 저장(create)하거나 업데이트(update)함
  • 유효성 검증에 통과한 값들로, 지정된(연결되어 있는) Model instance로의 저장(save)이 가능. 저장된 객체가 이미 존재한다면(pk가 이미 있다면) update되고, 그렇지 않으면 create됨

 

일반 Form과 Model Form의 차이

  • 일반 Form은 만들 field를 일일이 설정하고(title,content..), 그 field들의 속성도 설정해줘야 함(Charfield같은 것)
  • 모델 Form은 어떤 모델과 연결할지, 그리고 연결된 모델의 어떤 필드를 사용할지만 정해주면 됨(핵편함)
  • 실제로 모델과 연결될 경우 장고에서도 일반 Form보다는 Model Form을 사용할 것을 권함
  • Serializer에도 ModelSerializer가 있으며, 역시 Model과 연결될 경우 Modelserializer를 쓰는 것이 편함
#forms.py
from django	import forms
from .models	import Post

#일반 form
class PostForm(forms.Form):
    title	=forms.CharField()
    content	=forms.CharField(widget=forms.Textarea)

#모델 form
class PostModelForm(forms.ModelForm):
    class Meta:
        model	=Post
        fields	=['title','content']

 

ModelForm.save()

  • 다음과 같은 view의 로직이 있다고 하자. 여기서 PostForm은 models.py에서 설정된 modelform이다.
  • form.is_valid()가 실행되면 form의 모든 필드들에 대해 유효성 검사가 실행된다. 모든 필드들에 대한 유효성 검사가 성공적으로 끝나면 form.is_valid()는 True를 반환하며 유효성 검사가 실행된 값들은 form.cleaned_data에 담긴다
  • 이때 modelform.save()를 실행시, form.cleaned_data에 담긴 값들을 기반으로 연결된 model의 instance를 생성하고 반환한다.
    • 아래의 예시에서 form.save()라고 되어있지만, 이 form이 modelform인 postform의 인스턴스임. 
    • form.save()의 결과로 리턴된 model의 객체를 post가 받음. 즉 post는 새로이 생성된 model의 인스턴스
    • post.save()를 통해 DB에 해당 객체가 저장됨
  • modelform.save()는 commit이라는 값을 받음
    • default = True로, True일 경우 자동적으로 model인스턴스.save()를 호출한다(즉 자동으로 db에 저장시킨다)
    • 아래의 예시에서는 form.save(commit=True)였을 경우 자동적으로 post.save()가 호출되어서 DB에 저장되었을 것임. 즉 'modelform.save()'는 instance.save()와 다른 것이다.
    • 그러면 commit=False를 왜 줄까? 코드만 길어지는데?
      • form에서 다 채우지 못한 정보를 채우기 위해 instance.save()호출을(즉 db저장을) 지연시키려는 것임
      • 예를 들어, post모델에서 반드시 입력해야 하는 작성자 정보는 form에서 입력할 수 있으면 안됨(작성자를 조작할 수 있으므로)
      • 그러면 form에서 author 필드를 제외시키는데, 모델 instance가 db에 저장되기 위해서는 반드시 author가 어디선가 입력되어야 함
      • 따라서 form에서 입력하지 못한 값들을 채우기 위해 일단 객체만 지정을 해서 받아두고, 받은 객체에 입력하지 못한 field의 값을 채워준 후에 instance.save()로 db에 저장하는 것임
      • 의문)근데 field가 다 없으면 어케 유효성 검사(is_valid)가 통과하는 것임? A : form에 설정된 필드에 한해서만 유효성 검사를 진행함
  • 참고)modelform에서 단순히 받은 정보를 새로이 생성하여 저장하기만 원한다면, form = postForm(request.POST) 등으로 form에 데이터를 넣어주고 form.save()만 해도 데이터는 저장이 됨
#forms.py
def post_new(request):
    if request.method =="POST":
        form = PostForm(request.POST,request.FILES)
        if form.is_valid(): #form에 설정된 필드에 한해서만 유효성검사를 진행
        	
            #form필드에 다른 정보의 입력이 필요없다면 이렇게만 해도 db에 내용이 저장된다
        	#form.save()
        
            post = form.save(commit=False)
            post.author = request.user #request User = 현재 로그인 유저 인스턴스
            post.save()
            
            return redirect(post)
            #get_absolute_url을 찾아감
    else:
        form = PostForm()

 

ModelForm을 활용한 수정 

  • get요청이 들어와서 빈 form을 줄때나 post로 온 요청데이터를 form에 다시 담을 때 모두 form instance를 만드는 과정이 필요하다
  • 만약 Form이 ModelForm이라면, form instance를 만들 때 만들어진 form이 model의 어떤 instance와 연결되는지를 선언해주면, 해당 form instance를 통해 수행한 작업들이 연결된 model instance에 반영된다.
def post_edit(request,pk):
    post = get_object_or_404(Post,pk=pk)
    if request.method =="POST":
        form = PostForm(request.POST,request.FILES,instance=post)
        #지금 값을 넣은 form객체가 모델form의 어느 instance와 연결이 되어있는지를 가르쳐주는 거임

        if form.is_valid():
            post = form.save()

            return redirect(post)
            #get_absolute_url을 찾아감
    else:
        form = PostForm(instance=post)
        #instance=post를 줬기 때문에 form에 get요청을 하면 instance의 값들이 들어가있는 채로 바로 나옴
        
    return render(request,'instagram/post_form.html',{
        'form':form,
    })

 

주의사항

  • Form을 끝까지 사용해야 한다
  • form에는 유효성검사(is_valid)가 끝이나면 자동으로 'clean_필드명' 함수를 호출하는 경우가 있음
  • clean_필드명 함수는 커스터마이징된 유효성 검사와 더불어, 값을 조정하는 경우가 있음(문자열만 남긴다 등)
  • clean_필드명 함수의 결과값이 자동으로 cleaned_data에 반영이 되므로, cleaned_data에서 값을 꺼내오는 방식이 로직 전체를 적용받을 수 있어서 훨씬 안전하다!
  • request.POST에서 값을 꺼내오면, clean_필드명 함수로직을 타지 못할 수도 있음!!
#비추천방식
...
if form.is_valid()
	message= request.POST['message'] 
    
#추천방식
...
if form.is_valid()
	message= form.cleaned_data['message']

BELATED ARTICLES

more