파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트 강의를 듣고 정리한 글입니다.
일반폼 또는 모델폼을 정의한다. Model이 데이터베이스와 인터렉션 하듯이, Form은 HTML Form과 관계되어 있다.
# forms.py
from django import forms
from .models import Post
# 모델 폼 예시
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content'] # fields는 명시적으로 정의하는 것을 추천
# 일반 폼 예시
class PostForm(forms.Form):
title = forms.CharField()
content = forms.CharField(widget=forms.Textarea)
def save(self, commit=True):
# ModelForm.save 인터페이스를 흉내내어 구현
post = Post(**self.cleaned_data)
if commit:
post.save()
return post
모델폼은 유효성 검증을 통과한 값들을 지정 Model Instance로의 저장을 지원한다.
정확히는 save()
메서드를 통해서 Form의 cleaned_data를 Model Instance 생성에 사용하고, 그 Instance를 반환한다.
save()메서드는 commit인자(default=True
)를 받는다. commit 인자는 모델 인스턴스의 save()를 호출할지 결정하는 파라미터다.
@login_required
def post_new(request):
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
post = form.save(commit=False) # 모델 인스턴스의 save()를 호출하지 않는다.
post.user = request.user # 유저 정보를 저장한다.
post.save() # 유저 정보 저장 후 모델 인스턴스의 save()를 호출하도록 한다.
return redirect(post)
else:
form = PostForm()
return render(request, 'instagram/post_form.html', {
'form': form
})
request.method : 요청의 종류 ("GET" 또는 "POST"로서 모두 대문자다)
request.POST: POST 인자 목록 (QueryDict 타입)
request.GET: GET 인자 목록 (QueryDict 타입)
request.FILES: POST 인자 중에서 파일 목록 (MultiValueDict 타입)
request.body: 순수 body 데이터 (json요청 등). POST, GET, FILES는 http의 body부분을 해석해 생성한 객체인데 반해 request.body는 순수한 body 데이터를 그대로 제공한다.
request.META: http 헤더를 해석한 데이터
# views.py
from .forms import PostForm
def post_new(request):
if request.method == 'POST':
# POST 요청일 때 처리
# request.POST, request.FILES를 제공받는다.
form = PostForm(request.POST, request.FILES)
if form.is_valid(): # 인자로 받은 값에 대해 유효성 검증이 성공하면 True
# 검증에 성공한 값들을 사전타입(cleaned_data)으로 제공받는다.
# 필요에 따라 이 값을 데이터베이스에 저장한다.
post = form.save()
# success url로 이동하도록 하기
return redirect(post) # Post에 get_absolute_url이 구현되어 있기에 객체만 넘기면 된다.
else:
# GET 요청일 때 빈 폼을 생성한다.
form = PostForm()
# 만약 검증에 실패하면 form.errors에 오류 정보가 저장된다.
return render(request, 'instagram/post_form.html', {
'form': form
})
<!-- post_form.html -->
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{ form }}
</table>
<input type="submit" value="저장" />
</form>
# 일반 폼 예시
class PostForm(forms.Form):
title = forms.CharField()
content = forms.CharField(widget=forms.Textarea)
def save(self, commit=True):
# ModelForm.save 인터페이스를 흉내내어 구현
post = Post(**self.cleaned_data)
if commit:
post.save()
return
# 모델 폼 예시
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields =
post
유효성 검사는 form.is_valid()가 호출되는 시점이 수행된다.
유ㅜㅎ
form.full_clean()
호출필드객체.clean()
호출을 통해 각 필드 Type에 맞춰서 유효성 검사 수행Form객체.clean_필드명()
함수가 있다면 호출해서 유효성 검사Form객체.clean()
함수가 있다면 유효성 검사form.isvalid()
에서 True/False를 리턴clean
, clean_멤버함수
를 통해 유효성 검사를 수행하고 결과 값을 반환한다.# views.py
@login_required
def post_new(request):
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid(): # 유효성 검사 수행 후 True/False를 반환
post = form.save(commit=False)
post.user = request.user
post.save()
return redirect(post)
else:
form = PostForm()
return render(request, 'instagram/post_form.html', {
'form': form
})
# forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['message', 'photo', 'tag_set', 'is_publish']
def clean(self): # Form객체.clean() 함수
# ...
pass
def clean_message(self): # Form객체.clean_필드명() 함수
message = self.cleaned_data.get('message')
if message:
message = re.sub(r'[a-zA-Z]+', '', message)
return message
값이 원하는 조건에 맞지 않으면 ValidationError 예외를 발생시킨다. (결과 값을 반환하지는 않는다.)
모델 필드 또는 폼 필드를 정의 시에 지정한다.
빌트인 Validators를 사용하기를 추천한다.
사용자 정의 Validator는 마이그레이션 시 문제가 발생할 수 있다.빌트인 validators
- RegexValidator
- EmailValidator
- URLValidator
- validate_email
- validate_slug
- validate_unicode_slug
- validator_ipv4_address,validator_ipv6_address, validator_ipv46_address
- validator_comma_separated_integer_list
- int_list_validator
- MaxValueValidator
- MinValueValidator
- MaxLengthValidator
- MinLengthValidator
- DecimalValidator
- FileExtensionValidator : 파일 확장자 허용 여부
- validate_image_file_extension : 이미지 확장자 여부 (Pillow 설치 필수)
# validator
def phone_number_validator(value):
if not re.math(r'010[1-9]\d{7}$`):
raise ValidationError('{} is not an phone number'.format(value))
# model
class Profile(model.Model):
phone_number = models.CharField(max_length=11, validators=[phone_number_validator])
# Model Form
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = '__all__'
# basic Form
class ProfileForm(forms.Form):
phone_number = forms.CharField(validators=[phone_number_validator])