① Form 태그를 쉽게 만들 수 있는 기능
② 유효성 검사 기능

1. ModelForm : Model을 이용한 Form 생성

# forms.py

class NoticeForm(forms.ModelForm):		# forms.ModelForm 상속받음
    # 필드 추가 (직접 필드를 정의해도 됨)
    
    def __init__(self, *args, **kwargs):
        #생략
    
    class Meta:
        model = Notice			# 모델 연결
        fields = (			# 속성나열
            'name', 'title', 'content',
        )
        widgets = {			# default값 변경시
            'title' : forms.TextInput(attrs={'class': 'form-control'}),
            'content' : forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
        }
        label = {
            'name' : '이름',
            'title' : '제목',
            'content' : '내용',  
        }
      
    def save(self, commit=True):		# save() 오버라이딩
        instance = super().save(commit=False)
        #수정예정
        
        return instance
    
    def clean(self):						# clean() 오버라이딩
        cleaned_data = super().clean()
        title = cleaned_data.get('title')
        
        if '[공지]' in title:				# ValidationError 커스터마이징
            raise ValidationError('해당 문구는 사용할수 없습니다.')
        
        return cleaned_data
        
    
    
    # 위의 clean()을 아래와 같이 사용할 수도 있음
    def clean_title(self):
        title = self.cleaned_data.get('title')
        
        if '[공지]' in title:				# ValidationError 커스터마이징
             raise ValidationError('해당 문구는 사용할수 없습니다.')
    
        return title 
        

→ 폼을 구현하기 위해서는 forms.ModelForm을 상속 받아야 함
→ ModelForm은 내부클래스인 Meta가 반드시 필요 함

  • class Meta
    • Meta 클래스 안에 model과 fields를 지정하면 모델폼이 자동으로 폼필드를 생성 함   (model : 모델클래스 연결, fields : 폼에 사용할 속성 나열, label : 변수명 대신 출력시킬 값)
  • forms.ModelForm 메서드
    • save() :
    • clean() : 유효성검사 후 데이터를 받음
    • clean_필드명 : 해당 필드만 유효성검사

2. Form

# forms.py

class NoticeForm(forms.Form):		# forms.Form 상속받음
    name = forms.CharField(max_length=12, required=False, label='이름')
    title = forms.CharField(label='제목')
    content = forms.CharField(widget=forms.Textarea, lable='내용')      
    
    def __init__(self, *args, **kwargs):
      self.user = kwargs.pop('user')			### view로 부터 user 넘겨받기
      super().__init__(*args, **kwargs)
      self.fields['name'].widget.attrs.update({'class': 'form-control', 'value' : self.user.first_name})
      self.fields['title'].widget.attrs.update({'class': 'form-control'})
      
      
    def clean(self):
       #생략

→ 모델폼과는 다르게 필드를 직접 정의함

  • Form 필드 파라미터 (Model 필드 파라미터와 유사)
    • max_length : 최대글자수
    • required : 필수값
    • label : name, title, content와 같은 영어의 변수명 대신에 출력시킬 값
    • initial : 폼에서 입력시킬 초기값
    • validators : 유효성검사 (유효성 검사는 Model에서 하는걸 추천)
  • def __init__(self, *args, **kwargs) : 생성자
    • widget.attrs.update() : 위젯 속성값 변경
  • forms.Form 메서드
    • clean() : 유효성검사

View -> Form 데이터 넘기기

  • Form의 생성자의 파라미터로 데이터를 넘기면 됨

View

form = NoticeForm(user = request.user)				# GET방식
form = NoticeForm(request.POST, user = request.user) 	# POST방식

Form

class NoticeForm(forms.Form):
    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user')		# pop을 이용하여 받음	
        super().__init__(*args, **kwargs)

View

# views.py
from .forms import NoticeForm
from django.shortcuts import redirect

    def notice_create(request):
        if : request.method == "POST":		# POST방식
            form = NoticeForm(request.POST, user = request.user)
            if form.is_valid():
                notice = form.save(commit=False)	# 바로 저장하지 않고 꺼내와서 수정함
                notice.date = timezone.now()
                notice.save()				# 저장
                return redirect('notice')
        else : 					# GET방식
            form = NoticeForm(user = request.user)			### Form으로 user 매개변수 넘기기
            context =  {'form': form}
            return render(request, 'polls/notice.html', context)

→ form 속성에 사용할 폼 명시
→ GET방식일 경우 폼 출력, POST방식일 경우 DB에 저장
→ save(commit=False)를 사용하여 바로 Notice 모델에 저장하지 않고 수정을 할 수 있음
→ 수정이 끝나면 save()를 사용하여 저장

  • form.is_valid()
    • 검증에 성공한 값들은 사전타입으로 제공 (form.cleaned_data)
    • 검증에 실패시 form.error 에 오류 정보를 저장 -> 템플릿에서 object_list.errors 로 출력가능
  • form.cleaned_data
    • is_valid()를 통해 검증된 값
    • dict형태

○ 모델객체에 저장방법

① 디폴트 생성자 사용

if form.is_valid():
    notice = Notice()
    notice.title = form.cleaned_data['title']
    notice.content = form.cleaned_data['content']
    notice.save()

② 생성자 사용

if form.is_valid():
    notice = Notice(title = form.cleaned_data['title'],
    			content = form.cleaned_data['content'])
    notice.save()

③ create() 사용

if form.is_valid():
    notice = Notice.objects.create(title = form.cleaned_data['title'],
    					content = form.cleaned_data['content'])

Template


<!-- notice_create.html -->

{% extends 'blog/base.html' %}

{% block content %}
    <h1>Create Notice</h1>
    <form method="POST" class="post-form">
        {% csrf_token %}
        {{ form.as_p }}
        <button class="save btn btn-default">Save</button>
    </form>
{% endblock %}


→ {{ form.as_p }} : form의 속성에 맞게 자동으로 html 태그들을 생성시키고 각 항목을 p태그로 감싸서 폼을 만들어 줌

→ {{ form.as_ul }} : ul태그로 감쌈

→ {{ form.as_table }} : table태그로 감쌈

  • Form 형태를 커스텀 하고 싶을 경우

{% extends 'blog/base.html' %}

{% block content %}
    <h1>Create Notice</h1>
    <form method="POST" class="post-form">
        {% csrf_token %}
        <ul>
          <li><label for="name">이름</label></li>
          <li>
            {{ form.name }}
            {{ form.name.errors }
          </li>
        </ul>
        <ul>
          <li><label for="title">제목</label></li>
          <li>
            {{ form.title }}
            {{ form.title.errors }
          </li>
        </ul>
        <ul>
          <li><label for="content">내용</label></li>
          <li>
            {{ form.content }}
            {{ form.content.errors }
          </li>
        </ul>
        <button class="save btn btn-default">Save</button>
    </form>
{% endblock %}

<django official Documentation>
docs.djangoproject.com/en/3.1/ref/forms/fields/

docs.djangoproject.com/en/3.1/ref/forms/api/

docs.djangoproject.com/en/3.1/topics/forms/modelforms/#django.forms.ModelForm