지금까지는 HTML의 'Form' 태그를 이용해서 사용자로부터 데이터를 받았다. 그러나 이는 비정상적 혹은 악의적인 요청을 필터링 할 수 없다. (유효한 데이터인지에 대한 확인이 필요)
따라서 우리는 수집한 데이터가 정확하고 유효한지 확인하는 과정(유효성 검사)이 필요하다.
유효성 검사를 구현하기 위해서는 입력 값, 형식, 중복, 범위, 보안 등 많은 것들을 고려해야 하는데, Django가 제공하는 Form을 사용하여 구현할 수 있다.
Django Form
사용자 입력 데이터를 수집하고 처리 및 유효성 검사를 수행하기 위한 도구
→ 유효성 검사를 단순화하고 자동화 할 수 있는 기능을 제공
- 사용자로부터 데이터를 수집하고 처리하기 위한 강력하고 유연한 도구
- HTML form의 생성, 데이터 유효성 검사를 쉽게 할 수 있도록 도움
1. Form class
1.1 Form class의 정의
기본적으로 앱 폴더 안에 form.py 파일을 만들어서 정의한다.
모델을 정의할때와 유사한 구조를 가진다.
# article/form.py
from django import forms
class ArticleForm(forms.Form):
title= forms.CharField(max_length=10, required=False)
content=forms.CharField()
1.1 view 함수에서 작성혹은 변경
# article/view.py
from .forms import ArticleForm
def new(request):
# 게시글 작성 페이지 응답
form = ArticleForm() #인스턴스 생성
context = {
'form':form,
}
return render(request, 'articles/new.html',context)
1.2 new 페이지에서 form 인스턴스 출력
사전에 from 태그안에 form 인스턴스를 넣는다
토큰과 submit은 지우지 않는다!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>New</h1>
<form action="{% url "articles:create" %}" method="POST">
{% csrf_token %}
{{form}}
<input type="submit">
</form>
</body>
</html>
Django form 사용전 new.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>New</h1>
<form action="{% url "articles:create" %}" method="POST">
{% csrf_token %}
<div>
<label for="title">Title: </label>
<input type="text" name="title" id="title">
</div>
<div>
<label for="content">Content: </label>
<textarea name="content" id="content"></textarea>
</div>
<input type="submit">
</form>
</body>
</html>
Form rendering options
label, input 쌍을 특정 HTML 태그로 감싸는 옵션
- form.as_p
- form.as_div
- form.as_ul
- form.as_table
https://docs.djangoproject.com/en/4.2/topics/forms/#form-rendering-options
Widgets
HTML 'input' element의 표현을 담당
- 단순히 input의 표현을 바꾸는 용도로 사용한다.
- form 필드의 인자로 사용해야 한다.
Django는 기본적으로 두개의 Form을 제공한다.
Form VS ModelForm
Form
사용자 입력 데이터를 DB에 저장하지 않을 때 (ex 검색, 로그인)
ModelForm
사용자 입력 데이터를 DB에 저장해야 할때 (ex 게시글 작성, 회원가입)
2. ModelFrom
Form + Model
Model과 연결된 Form을 자동으로 생성해주는 기능을 제공(만들어진 모델을 기반으로 만들 수 있어 코드작성중복을 줄임)
type, content를 재정의 필요 X
Meta 클래스라고 하는 이유?
- Meta가 데이터의 데이터라는 의미이기 때문에
- 사진의 예로들면 사진 안에도 위치, 조리개값, 조도, 타입 등이 들어있는데 사진도 데이터이고 그안에 값도 데이터인데 이런 것을 Meta data라고 한다
- 따라서 모델 폼에 대한 정보를 작성하는 공간이라 이름을 Meta 라고 했다
https://docs.djangoproject.com/en/5.1/topics/forms/modelforms/
사용법
2.1 ModelForm 작성
# article/forms/py
from django import forms
from .models import Article
# class ArticleForm(forms.Form):
# title= forms.CharField(max_length=10)
# content=forms.CharField(widget=forms.Textarea)
class ArticleForm(forms.ModelForm):
class Meta: # 모델 폼에 대한 정보를 메타 클래스안에 작성
model = Article # 어떤 모델을 기반으로 할 것인지
fields= '__all__' # 어떤 필드를 사용할 것인지
# exclude=('title',)
Meta class
- Model Form의 정보를 작성하는 곳
- 'fields' 는 field를 지정하는 것
- 'exclude' 는 field를 제외하는 것
- 주의 사항
- Django에서 ModelForm에 대한 추가 정보나 속성을 작성하는 클래스 구조를 Meta 클래스로 작성했을 뿐이니 파이썬의 inner class와 같은 문법적인 관점으로 접근하지 말 것
2.2 view 함수 작성
실질적으로 유효성검사는 view 함수에서 실시 한다.
- 모델 폼 인스턴스 생성
- if 문과 is_valid() 매서드를 사용해서 유호성 검사
- 성공시 저장후 redirect (save 메서드에 return값에 저장된 데이터 정보가 담겨있다)
- 실패시 실패 내용을 담아 다시 rendering (is_valid 메서드에서 실패시에 실패 내역이 담긴다.)
def create(request):
# 모델폼 사용전
# title = request.POST.get('title')
# content = request.POST.get('content')
# 저장 2
# article = Article(title=title, content=content)
# article.save()
# 1. 모델폼 인스턴스 생성 (+ 사용자 입력 데이터를 통째로 인자로 작성)
form = ArticleForm(request.POST)
# 2. 유효성 검사
if form.is_valid(): # 실패하면 실패 내역 담김
article = form.save()
return redirect('articles:detail', article.pk)
# 실패시
context = {
'form': form,
}
return render(request, 'articles/new.html', context)
참고
수정의 경우 수정 이전 데이터와 작성한 데이터를 불러와야 하기 때문에 살짝 코드가 다르다
def edit(request, pk):
# 모델 폼 사용전
# article = Article.objects.get(pk=pk)
# context = {
# 'article': article,
# }
# return render(request, 'articles/edit.html', context)
article = Article.objects.get(pk=pk)
form =ArticleForm(instance=article)
context = {
'article': article,
'form':form
}
return render(request, 'articles/edit.html', context)
def update(request, pk):
# 모델 폼 사용전
# # 1. 어떤 게시글 수정할지 조회
# article = Article.objects.get(pk=pk)
# # 2. 사용자로부터 받은 새로운 입력 데이터 추출
# title = request.POST.get('title')
# content = request.POST.get('content')
# # 3. 기존 게시글의 데이터를 사용자로 받은 데이터로 새로 할당
# article.title = title
# article.content = content
# # 4. 저장
# article.save()
article =Article.objects.get(pk=pk)
# 1. 모델 폼 인스턴스 생성 (+ 사용자 입력 데이터 & 기존 데이터)
form = ArticleForm(request.POST, instance=article)
# 2. 유효성 검사
if form.is_valid():
form.save()
return redirect('articles:detail', article.pk)
context = {
'article': article,
'form': form,
}
return render(request, 'articles/edit.html', context)
is_valid()
- 여러 유효성 검사를 내부적으로 실시하고 데이터가 유효한지 여부를 Boolean으로 반환
- false 리턴시 false 원인도 같이 return을 해준다
- 과정
- 별도로 명시하진 않았지만 모델 필드에서 기본적으로 빈 값은 허용하지 않는 제약 조건이 설정되어 있음
- 빈 값은 is_valid()에 의해 False로 평가되고 form 객체에는 그에 맞는 에러 메시지가 포함되어 다음 코드로 진행
save()
데이터베이스 객체를 만들고 저장하는 ModelForm의 인스턴스 메서드
키워드 인자 instance 여부를 통해 생성할 지, 수저할 지 결정한다.
HTTP 요청 다루기
new 와 create view 함수 간의 공통점 차이점
공통점
- 데이터 생성을 구현하기 위함
차이점
- new는 GET method 요청만을
- create는 POST method 요청만을 처리
따라서 우리는 HTTP request method 차이점을 활용해 동일한 목적을 가지는 2개의 view 함수를 하나로 구조화 할 수 있다!!
생성을 위해 만들었던 new와 create 함수의 결합
결합 전
def new(request):
# 게시글 작성 페이지 응답
form = ArticleForm() #인스턴스 생성
context = {
'form':form,
}
return render(request, 'articles/new.html',context)
def create(request):
# 1. 모델폼 인스턴스 생성 (+ 사용자 입력 데이터를 통째로 인자로 작성)
form = ArticleForm(request.POST)
# 2. 유효성 검사
if form.is_valid(): # 실패하면 실패 내역 담김
article = form.save()
return redirect('articles:detail', article.pk)
# 실패시
context = {
'form': form,
}
return render(request, 'articles/new.html', context)
결합 후
- 두 함수의 유일한 차이점이었던 request method에 따른 분기
- POST 아닐 때는 과거 new 함수에서 진행 했던 form 인스턴스 생성
- context에 담기는 form은
- is_valid()를 통과하지 못해 에러메시지를 담는 form이거나
- else 문을 통한 form 인스턴스
def create(request):
# 요청 메서드가 POST일 때
if request.method == 'POST':
# 1. 모델폼 인스턴스 생성 (+ 사용자 입력 데이터를 통째로 인자로 작성)
form = ArticleForm(request.POST)
# 2. 유효성 검사
if form.is_valid(): # 실패하면 실패 내역 담김
article = form.save()
return redirect('articles:detail', article.pk)
# 요청 메서드가 POST가 아닐 때(PUT,DELETE 등 다른 메서드 존재)
else:
form = ArticleForm() #인스턴스 생성
context = {
'form':form,
}
return render(request, 'articles/new.html',context)
이제 new와 관련된 코드를 모드 수정하면 끝난다(url들 모드 수정)
- new의 존재들을 모드 지우기- (create)로 바꾼다.
생성을 위해 만들었던 edit와 update 함수의 결합
결합 후
def update(request):
article = Article.objects.get(pk=pk)
if request.mothod == 'POST':
form = ArticleForm(request.POST, instance=article)
# 2. 유효성 검사
if form.is_valid():
form.save()
return redirect('articles:detail', article.pk)
else:
form =ArticleForm(instance=article)
context = {
'article': article,
'form':form
}
return render(request, 'articles/update.html', context)
참고 자료
ModelForm의 키워드 인자
- data
- data는 첫 번째에 위치한 키워드 인자이기 때문에 생략 가능
- instance
- instance는 9번째에 위치한 키워드 인자이기 때문에 생략할 수 없었음
Widgets 응용
- {{form.as_p}}의 표현을 바꿀 수 있도록 하게 하기 위해서 widgets을 사용
- 필드를 수동으로 렌더링 할 수 도 있다
class ArticleForm(forms.ModelForm):
# 메타클래스 위에 쓰는 걸 권장
title = forms.CharField(
label='제목',
widget=forms.TextInput(
attrs={
'class': 'my-title',
'placeholder': '제목을 입력해주세요',
'maxlength':10,
}
)
)
class Meta:
model = Article # 어떤 모델을 기반으로 할 것인지
fields= '__all__' # 어떤 필드를 사용할 것인지
https://docs.djangoproject.com/en/5.1/ref/forms/widgets/
'Django' 카테고리의 다른 글
Django Template & URLs [Django] (0) | 2024.09.29 |
---|---|
Static [Django] (3) | 2024.09.26 |
Django ORM with view [Django] (0) | 2024.09.24 |
Django ORM [Django] (3) | 2024.09.23 |
Django Model 생성하기[Django] (0) | 2024.09.23 |