본문 바로가기
개발/Django_python 웹 프레임워크

ModelForm - Forms.py

by 자유로운 코끼리 2020. 7. 25.
728x90

Form, 우리는 Form을 언제 사용하였죠?

우리는 html에서 입력 값을 받고 넘기기 위해 form 태그를 만들어줬는데,

이때 이모저모 신경 써줄 것들이 존재합니다.

 

우리는 모델에 정의해줬던 속성들 그대로 html을 만들었습니다.

제목과 날짜, 본문을 입력하고 model 객체를 생성해 데이터를 집어넣어 저장했습니다.

model을 바꾸거나 추가하게 되면 그에 맞는 form태그를 수정해줘야 하고,

model의 일부분만 입력받고 싶고, 또 어떤 공간에서는 model의 전체를 입력받고 싶을 때

그때마다 일일이 form 태그를 만들어주었었죠.

 

하지만 이제는 장고에서 제공해주는 form.py을 이용해 간단하게 바꿔 보겠습니다.

model을 기반으로 한 form을 이용하면 model 맞춤형 form을 구성해주고,

보다 편리하게 저장도 할 수 있습니다.

또한 템플릿에서 form을 만드는 것보다 보안에 훨씬 유리합니다.

 

 장고 폼 기능은 3가지로 요약할 수 있습니다.

  • 렌더링을 위해서 데이터를 준비하고 재구성을 해줍니다.
  • HTML 폼을 만들어줍니다.
  • 클라이언트로부터 제출이 된 데이터를 받고 처리해줍니다.

 

models.py가 DB와 연결되었던 것처럼

form.py는 html form태그와 매핑됩니다.

 

html form 태그

<form action="" method="post">
    Your name: 
    <input type="text" name="your_name" value="{{ current_name }}">
    <input type="submit" value="OK">
</form>

우리는 여태 이런 방식으로 form을 이용해주었습니다.

하지만 이때 데이터가 작성이 되어있지 않거나 하는 등의 일이 생기면 

화면은 넘어가도 나중에 오류가 나게 됩니다.

이를 위해 유효성 검사를 해주어야 하는데,

이러한 역할들을 손쉽게 해주는 것이 쟝고에서 제공해주는 form입니다.

Form vs ModelForm

이때 모델을 기반으로 입력공간을 만들 수도 있고,

임의의 입력공간을 만들 수도 있습니다.

임의의 입력공간의 경우 model에서 만든 것 이외에도 공간을 만들고 싶을 때 이용한다고 합니다.

오늘은 모델을 기본으로 한 입력공간을 만들어보겠습니다.

#임의의 입력공간만들기
from django import forms.Form

class myForm(forms.Form):
	text = forms.TextField
#forms.py
from django import forms
from .models import Blog

class BlogPost(forms.ModelForm):
    class Meta:
        model = Blog # 어떤 모델을 기반으로 한 입력공간이니?
        fields = ['title','body'] # 그 모델 중에서 어떤 항목을 입력받을거니?
          

모델을 기본으로 한 입력공간

#forms.py
from django import forms
from .models import Blog

class BlogPost(forms.ModelForm):
    class Meta:
        model = Blog # 어떤 모델을 기반으로 한 입력공간이니?
        fields = ['title','body'] # 그 모델 중에서 어떤 항목을 입력받을거니?
          

우리는 모델을 기반으로 하는 클래스를 제작해야 하니

forms클래스의 ModelForm을 인자로 넘겨 줍니다.

 

이제 메타클래스를 선언하고, 그 안에 model과 fields를 정의해보겠습니다.

메타클래스는 클래스를 만들어주는 클래스입니다.

자세한 내용은 구글에 참고하는 걸로 ㅎㅎ

 

meta 클래스 내에는 어떤 모델을 기반으로 Form을 생성할지 model 에 작성합니다.

그리고 모델의 속성들 중 입력받길 원하는 속성만 fields 에 작성해주세요.

날짜 데이터 같은 경우는 굳이 입력받을 필요는 없으니까요.!

 

여기서는 models.py에 정의한 Blog클래스를 모델으로 하고,

제목과 본문만 입력받길 원하니 위처럼 작성했습니다.

날짜는 사용자가 직접 입력하지 않아도 views.py에서 집어넣게끔 구현해봅시다.

 

 

이제 열심히 만든 forms.py를 사용할 차례입니다!

from .forms import BlogPost

def blogpost(request):
    if request.method =='POST':
     #POST방식으로 요청이 들어왔을 때 - form에 입력받은 데이터를 저장하기
        form = BlogPost(request.POST) #입력된 내용들을 form이라는 변수에 저장
        if form.is_valid(): #form이 유효하다면(=models.py에 정의한 필드에 맞다면)
            post = form.save(commit=False) #form 데이터를 가져온다.-> 날짜랑 사용자를 넣어줘야 하니까!
            post.pub_date=timezone.now()
            post.save()
            #form.save(pub_date=timezone.now()) 
            return redirect('home')
    else:
    #GET방식으로 요청이 들어왔을 때 - form을 보여주기
        form = BlogPost()
        return render(request,'new.html',{'form':form})

GET방식으로 요청이 날아온다면 :

BlogPost클래스의 객체를 만들어 변수에 넣어 줍니다.

그리고 html을 띄울 때 딕셔너리 자료형으로 넣어 전달합시다.

 

POST방식으로 요청이 날아온다면 

먼저 form에 BlogPost객체를 만들고, 

요청으로부터 받은 데이터와 폼을 묶어(바인딩) 줍니다.

 

is_valid() 함수는 form 인스턴스가 가지고 있는 함수로,

입력 받은 값들이 입력이 되었는지,

입력이 되었다면 형식에 잘 막춰졌는지를 검사하는 함수입니다.

유효하다면 cleaned_data에 값이 저장이 됩니다.

 

만약 유효하다면 post = form.save(commit=False) 로

form에 입력된 데이터들을 가져옵니다.

어? 원래 save는 저장하는 함수죠? 

하지만 인자에 commit=False 을 입력하면,

저장하지 않고 form 데이터만 가져올 수 있습니다.

 

바로 저장하지 않고 데이터만 받아오는 이유는

우리가 시간을 입력받지 않았기 때문입니다.

 

넣어주고 저장하면 끝!

 

이제 이를 html에 띄워볼께요.!

#create.html
<form method='POST' action="{% url 'post_form' %}">
    {% csrf_token %}
    {{form.as_table}}
    <input class="btn btn-dark" type="submit" value="제출하기">
</form>

html이 간결해졌습니다.

table 태그로 감싼 뒤 템플릿 태그를 통해 form을 출력합니다.

.as_table은 form의 내용(입력공간)이 table형식으로 출력하겠다는 의미의 메소드.

.as_p은 form의 내용(입력공간)이 p(문단)형식으로 출력하겠다는 의미의 메소드. 

.as_ul은 form의 내용(입력공간)이 ul(리스트)형식으로 출력하겠다는 의미의 메소드.

 

자 이때 Title,Body와 같은 부분을 바꾸는 등의 작업을 하고 싶다면,

forms.py에 다음과 같은 작업을 해주면,

title과 body을 바꿀 수 있습니다.

class BlogPost(forms.ModelForm):
    class Meta:
        model = Blogapp # 어떤 모델을 기반으로 한 입력공간이니?
        fields = ['title','body'] # 그 모델 중에서 어떤 항목을 입력받을거니?

    def __init__(self,*args,**kwargs): 
        #*args : 복수의 인자를 받고자 할 때, *뒤 변수명을 적으면 된다. 
        #**kwargs : 여러 키워드 파라미터를 받을 수 있다(ex- x=10 과 같은!).   
        super().__init__(*args,**kwargs)
        #super()을 이용해 부모클래스의 내용을 가지고 올 수 있다.=> 오버라이딩 즉 Blogapp 내용에 접근!
        self.fields['title'].label="제목"
        self.fields['body'].label="본문"
        self.fields['title'].widget.attrs.update({
            'class' : 'title_class',
            'placeholder':'제목을 입력하세요',
        })

Overrride 란? 재정의 한다고 하며, 부모 클래스에서 정의한 메서드를 자식 클래스에서 변경하는 것입니다.

 

 

Update

#views.py

def update_form(request,blog_id):
    content_update=Blogapp.objects.get(pk=blog_id) #get()은 dictionary의 요소 하나를 반환한다.
    if request.method=="POST":
        update_form =BlogPost(request.POST,instance=content_update)
        if update_form.is_valid():
            update_form.save()
            return redirect('detail',blog_id)
    else:
        form=BlogPost(instance=content_update)        
        return render(request,'update_form.html',{'update_form':form,'blog_id':blog_id})

Blogapp.objects.get(pk=blog_id)을 통해서 Blogapp클래스 모델에 있는 objects들 중에서

blog_id를 가진 객체를 하나 반환해서 content_update에 넣어줍니다.

 

우선 Get방식으로 호출되었을 때 먼저 살펴보겠습니다.

BlogPost로 form을 보여줄 때, instance를 넣어서 보내주면,

해당 객체의 값들이 들어가져서 화면에 보여지게 됩니다.

이때 제출 할 때를 생각하여 blog_id도 넘겨줍니다.

 

이제 제출을 누른, Post방식일 때는 폼에서 instance해당값을 가진 폼에서 수정된 값을 가져와

update_form에 넣어줍니다.

 

그리고 is_vaild()를 통해 유효성 검사를 시행한뒤,

저장해줍니다.

 

#update_form.html

<form action="{% url 'update_form' blog_id  %}" method="POST">
    {% csrf_token %}
    {{update_form}}
    <br>
    <input type="submit" value="제출하기">
</form>

#urls.py

    path('blog/update_form/<int:blog_id>',blogapp.views.update_form,name="update_form"),

댓글