DevYoon
[Django] Form 본문
Django Form
1️⃣ Django의 Form을 사용하는 이유 ➡️ 유효성 검증
- 유효성 검증 : 사용자가 입력한 데이터를 검증하는 것
- HTML의 form, input 태그를 사용해서 직접 사용자로부터 데이터를 받으면 유효성 검증을 코드로 모두 구현해야 함
- Django의 Form은 이러한 작업과 반복 코드를 줄여줌
- 외부의 악의적 공격 및 데이터 손상에 대한 중요한 방어 수단
2️⃣ Django가 처리해주는 form에 관련된 작업 3가지
- 렌더링을 위한 데이터 준비 및 재구성
- 데이터에 대한 HTML Forms 생성
- 클라이언트로부터 받은 데이터 수신 및 처리
3️⃣ Django Forms
3️⃣-1️⃣ forms 선언하기
앱 안에 forms.py 파일 생성
from django import forms class ArticleForm(forms.Form): title = forms.CharField(max_length=10) content = forms.CharField()
Model 선언과 유사, 같은 필드타입(CharField) 사용
forms 라이브러리에서 파생된 Form 클래스 상속받음
3️⃣-2️⃣ forms 사용하기
views.py 수정
from .forms import ArticleForm def new(request): form = ArticleForm() context = { 'form': form, } return render(request, 'articles/new.html', context)
template 수정
{{ form.as_p }}
➡️ form 안의 title, content 등의 내용을 {{ form }}으로 대체할 수 있음
*️⃣ Form rendering options
➡️ label & input 태그 쌍에 대한 출력 옵션
- as_p() : 각 필드가 단락(p 태그)로 감싸져서 렌더링 됨
- as_ul() : 각 필드가 목록 항목(li 태그)로 감싸져서 렌더링 되며, ul 태그는 직접 작성해야 함
- as_table() : 각 필드가 테이블(tr 태그)로 감싸져서 렌더링 되며, table 태그는 직접 작성해야 함
*️⃣ Django의 HTML input 요소 표현 방법
Form fields
- input에 대한 유효성 검사 로직을 처리하여 템플릿에서 직접 사용됨
Widgets
웹 페이지의 HTML input 요소 렌더링
GET/POST 딕셔너리에서 데이터 추출
위젯은 반드시 Form fields에서 할당 됨
💥주의
- Form Fields와 혼동 X
- Form Fields는 input 유효성 검사를 처리
- 위젯은 웹페이지에서 input element의 단순 raw한 렌더링 처리
content = forms.CharField(widget=forms.Textarea)
FRUITS_A = 'ap' # 서버에 넘어가는 것 FRUITS_B = 'or' FRUITS_C = 'gr' FRUITS_CHOICES = [ (FRUITS_A, '사과'), # 사과 - 사용자에게 보여주는 것 (FRUITS_B, '오렌지'), (FRUITS_C, '포도'), ] content = forms.ChoiceField(choices=FRUITS_CHOICES, widget=forms.Select())
4️⃣ ModelForm
- Model에 정의한 필드를 유저로부터 입력받기 위해 Form에서 Model필드를 재정의하는 행위가 중복될 수 있음
- Model을 통해 Form Class를 만들 수 있는 ModelForm이라는 일종의 도우미를 제공
- 일반 Form Class와 같은 방식(객체 생성)으로 view에서 사용 가능
4️⃣-1️⃣ ModelForm 선언하기
forms.py 안에 코드 작성
from .models import Article class ArticleForm(forms.ModelForm): class Meta: model = Article fields = '__all__' exclude = ('title',) # 출력에서 제외할 요소 지정
forms 라이브러리에서 파생된 ModelForm 클래스를 상속받음
정의한 클래스 안에 Meta 클래스 선언 후 어떤 모델을 기반으로 Form을 작성할 것인지 지정해줌
💥 주의 : fields와 exclude는 동시에 쓰일 수 없음!
*️⃣ Meta Class
- Model의 정보를 작성하는 곳
- ModelForm 사용 시 사용할 모델이 있어야 하는데, 이를 Meta Class가 구성
- field 정보를 Form에 적용
[참고] Inner Class (Nested Class)
- 클래스 내에 선언된 다른 클래스
- 관련 클래스를 함께 그룹화하여 가독성 및 프로그램 유지 관리를 지원
- 외부에서 내부 클래스에 접근할 수 없으므로 코드의 복잡성을 줄일 수 있음
4️⃣-2️⃣ view 수정
def create(request):
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save()
return redirect('articles:detail', article.pk)
return redirect('articles:new')
*️⃣ is_valid() method
- 유효성 검사를 실행하고, 데이터가 유효한지 Boolean으로 반환
*️⃣ save() method
- Form에 바인딩 된 데이터에서 데이터베이스 객체를 만들고 저장
- ModelForm의 하위 클래스는 기존 모델의 인스턴스를 키워드 인자 instance 로 받아들일 수 있음
- instance가 제공되면 save()는 해당 인스턴스를 수정
- instance가 제공되지 않으면 save()는 지정된 모델의 새 인스턴스를 생성
- Form의 유효성이 확인되지 않은 경우, save()를 호출해 form.errors를 통해 에러 확인 가능
4️⃣-3️⃣ view 수정2
# 같은 주소로 왔을 때
def create(request):
if request.method == 'POST': # GET인지 POST인지 확인
# create
form = ArticleForm(request.POST)
# 유효성 검사
if form.is_valid():
article = form.save()
return redirect('articles:detail', article.pk)
else:
# new
form = ArticleForm()
context = {
'form': form,
}
return render(request, 'articles/create.html', context)
4️⃣-4️⃣ 위젯 활용
방법 1
class ArticleForm(forms.ModelForm): class Meta: model = Article fields = '__all__' widgets = { 'title' : forms.TextInput(attrs={ 'class':'my-title', 'placeholder': '입력하세요', 'maxlength': 10,}, ) }
방법 2 (권장)
class ArticleForm(forms.ModelForm): title = forms.CharField( label='title', widget=forms.TextInput( attrs={ 'class':'my-title form-control', 'placeholder':'Enter the title',} ), )
💥 Form Class는 어느 위치에 두어도 상관 없지만, 되도록 앱/forms.py에 작성하는 것이 일반적
5️⃣ Form과 ModelForm 비교
Form
- 어떤 Model에 저장해야 하는지 알 수 없으므로 유효성 검사 이후 cleaned_data 딕셔너리 생성
- cleaned_data 딕셔너리에서 데이터를 가져온 후 .save() 호출
- Model에 연관되지 않은 데이터를 받을 때 사용
ModelForm
- Django가 해당 model에서 양식에 필요한 대부분의 정보를 이미 정의
- 어떤 레코드를 만들어야 할지 알고 있음 ➡️ 바로 .save() 호출 가능
✔️ 사용자의 정보를 입력 받음 ➡️ 저장 ➡️ Form과 DB가 밀접 ➡️ ModelForm
✔️ 사용자의 정보를 입력 받아 별도 처리 필요 ➡️ DB와 연관 x ➡️ Form
6️⃣ 수동으로 Form 작성하기
6️⃣-1️⃣ Rendering fields manually
<form action="{% url 'articles:create' %}" method="POST">
{% csrf_token %}
<div>
{{form.title.errors}}
{{form.title.label_tag}}
{{form.title}}
</div>
<div>
{{form.content.errors}}
{{form.content.label_tag}}
{{form.content}}
</div>
<br>
<input type="submit" value='CREATE'>
</form>
6️⃣-2️⃣ Looping over the form's fields
<form action="{% url 'articles:create' %}" method="POST">
{% csrf_token %}
{% for field in form %}
{{ field.errors }}
{{ field.label_tag }}
{{ field }}
{% endfor %}
<input type="submit" value='CREATE'>
</form>
6️⃣-3️⃣ Bootstrap 사용
Bootstrap Form의 핵심 class ➡️ forms.py의 widget에 작성
에러 메시지를 Bootstrap alert 컴포넌트
{% for field in form %} {% if field.errors %} {% for error in field.errors %} <div class="alert alert-warning" role="alert"> {{ error|escape }} </div> {% endfor %} {% endif %} <div class="form-group"> {{ field.label_tag }} {{ field }} </div> {% endfor %}
6️⃣-4️⃣ Django-bootstrap v5
form class에 bootstrap을 적용시켜주는 라이브러리
설치
$ pip install django-bootstrap-v5
등록
- settings.py ➡️ INSTALLED_APPS ➡️ 'bootstrap5'
적용
{% load bootstrap5 %} {% bootstrap_css %} {% bootstrap_javascript %} {% bootstrap_form form layout='horizontal'%}