장고6
Form
폼이란 사용자가 입력한 데이터를 서버로 전송하기 위한 방식을 말한다.
Get
서버의 데이터를 가져올 때 사용한다. CRUD에서 R에 해당한다.
데이터를 URL에 담아서 Request한다.
URL뒤에 쿼리 스트링에 담아 보낸다.
URL의 끝을 ?로 표시하고 데이터는 key=value로 이루어 지며 각각의 데이터는 &로 구분된다.
예시) google.com/profile?name=철수&age=17
이런 방식은 데이터가 URL에 노출되는 문제가 발생한다.
Post
서버의 데이터를 변경할 때 사용한다. CRUD에서 CUD에 해당한다.
데이터를 메시지의 안쪽에 담아 노출되지 않는다.
Form이 처리되는 과정
폼을 작성하기 위해 클라이언트가 서버로 폼양식을 요청한다.
폼이 있는 페이지를 처음 요청할 때는 단순히 조회하는 것이므로 get방식을 사용한다.
처음 서버가 제공하는 폼을 Unbound form이라 한다.
클라이언트에서 데이터를 입력하고 서버로 전송할 때는 post방식을 사용한다.
서버에서 입력된 데이터와 폼을 합친다. 이 과정을 바인딩이라 하며 이렇게 합쳐진 폼을 bound form이라 한다.
입력된 데이터가 올바르지 않다면 클라이언트에 다시 요청한다.
입력된 데이터가 유효하다면 서버에서 지정한 로직을 수행한다.
작업이 모두 끝나면 새로운 페이지를 응답한다.
HTML Form
<form>
<label for="title">제목</label>
<input type="text" id="title" name="title">
</form>
label과 input
폼은 폼 태그안에 사용자의 입력을 받는 input태그와 설명을 위한 label태그의 쌍으로 이루어 진다.
for와 id
label태그와 input태그를 묶어주기 위해 label태그에는 for속성, input태그에는 id속성이 사용된다.
만약 for와 id를 사용하지 않으려면 label태그로 input태그를 감싸면 된다.
<form>
<label>제목
<input type="text">
</label>
</form>
name
name은 입력된 데이터를 서버로 전송할 때 서버에서 각각의 데이터를 구분하기 위한 속성이다.
name속성이 있는 양식 요소만 값이 서버로 전달된다.
type
type은 입력할 값에 따른 유형을 나타낸다.
type에 따라 사용자가 브라우저에서 값을 입력하는 형식인 위젯이 달라진다.
- email
<label for="email">이메일</label> <input type="email" id="email" name="email">
- password
<label for="pwd">비밀번호</label> <input type="password" id="pwd" name="pwd">
- button
<input type="button" value="버튼입니다">
- radio
<input type="radio" id="male" name="gender" value="male"> <label for="male">남자</label><br>
- checkbox
<input type="checkbox" id="male" name="gender" value="male"> <label for="male">남자</label><br>
- date
<label for="birthday">생년월일</label> <input type="date" id="birthday" name="birthday">
- file
<label for="userfiles">파일선택</label> <input type="file" id="userfiles" name="userfiles" multiple>
- submit
<input type="submit" value="전송하기">
form속성
<form action="register" method="post">{% csrf_token %}
<label for="name">이름</label>
<input type="text" id="name" name="name">
<input type="submit" value="제출하기">
</form>
form에는 입력된 데이터를 전송할 서버의 URL을 지정하는 action과 http전달 방식을 지정해 주는 mothod속성이 있다.
action과 method를 작성하지 않으면 기본적으로 현재의 URL에 get방식으로 전송한다.
- get
- post
scrf는 웹 사이트에서 유저가 서버로 요청을 보내는 행위를 악의적으로 변경해서 요청을 전송하는 것이다.
장고에서는 {% csrf_token %}을 사용해서 위조 방지 토큰을 사용할 수 있다.
장고 폼
from django import forms
class PostForm(forms.Form):
title = forms.CharField(max_length=50, label='제목')
content = forms.CharField(label='내용', widget=forms.Textarea)
forms.py에 이렇게 폼을 작성하면 된다.
폼 필드는 그에 맞는 기본 위젯을 가지고 있고 필요에 따라 위젯을 따로 명시할 수도 있다.
예를들어 위 처럼 CharField에는 기본 위젯이 있지만 widget=forms.Textarea이렇게 다른 위젯을 쓸수가 있다.
필드
위젯
<form>{% csrf_token %}
{{form.as_ul}}
<input type="submit" value="전송">
</form>
템플릿에서 뷰에서 넘겨받은 폼을 리스트로 정렬해서 출력한다.
- .as_ul
리스트로 정렬해준다. - .as_table
테이블로 정렬해준다. - .as_p
p태그로 정렬해준다.
redirect
Create
# 템플릿
<form method="post">{% csrf_token %}
{{form.as_ul}}
<input type="submit" value="전송">
</form>
form속성에 action을 명시하지 않았으니 현재의 URL에 전송한다.
# 뷰
def post_create(request):
if request.method == 'POST':
new_post = Post( # 입력된 데이터를 가져와서 Page데이터모델을 만든다.
title = request.POST['title'],
content = request.POST['content']
)
new_post.save()
return redirect('post-detail', post_id=new_post.id)
else:
post_form = PostForm()
return render(request, 'posts/post_form.html', {'form':post_form})
처음 URL로 요청할 때는 get방식이므로 else문에서 폼을 생성해서 리턴한다.
폼을 작성하고 전송하면 현재의 URL로 다시 post방식으로 전송하게 한다.
그러면 이때는 post방식으로 들어오므로 if문으로 들어가서 작성한 데이터를 가져와 새로운 데이터 모델을 만들고 저장한다.
그리고 작성한 상세 페이지로 redirect시킨다.
모델 폼
폼은 모델과 구조가 비슷한데 웹 서비스에서의 폼은 모델을 기반으로 만든다. 이렇게 모델을 기반으로 폼을 만들 때 일일이 작성하지 않고 모델폼을 사용하면 되는데 모델폼은 장고의 기능으로 작성한 모델을 기반으로 자동으로 폼을 생성해준다.
class PostForm(froms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content']
Meta는 폼 클래스를 만들 때 여러 옵션을 넣는 클래스이다.
model은 어떤 모델을 참조할지 알려준다.
fields는 어떤 필드를 폼으로 만들지를 알려준다.
fields = ‘__all__‘하면 모든 필드가 적용된다.
def post_create(request):
if request.method == 'POST':
post_form = PostForm(request.POST) # 폼과 데이터를 바인딩
new_post = post_form.save()
return redirect('post-detail', post_id=new_post.id)
else:
post_form = PostForm()
return render(request, 'posts/post_form.html', {'form':post_form})
기존의 뷰에서 이렇게 수정해주면 된다.
def post_create(request):
if request.method == 'POST':
post_form = PostForm(request.POST)
if post_form.is_valid():
new_post = post_form.save()
return redirect('post-detail', post_id=new_post.id)
else:
post_form = PostForm()
return render(request, 'posts/post_form.html', {'form':post_form})
유효성 검사도 추가해 주자.
Update
def post_update(request, post_id):
post = Post.objects.get(id=post_id)
if request.method == 'POST':
post_form = PostForm(request.POST, instance=post)
if post_form.is_valid():
post_form.save()
return redirect('post-detail', post_id=post.id)
else:
post_form = PostForm(instance=post)
return render(request, 'posts/post_form.html', {'form': post_form})
처음 요청시는 get방식 이므로 else로 들어간다.
이때 수정을 할것이니 instance=post를 해야 새로운 빈 폼을 가져오지 않고 기존에 데이터를 사용하게 된다.
폼을 수정 후 전송하면 post방식으로 들어오니 if로 들어간다.
여기서도 마찬가지로 instance=post를 해줘야 기존의 데이터를 수정하게된다. 그렇지 않으면 수정된 내용을 기존의 데이터에 덮어쓰지 않고 새로 생성한 공간에 저장되게 된다.
Delete
def post_delete(request, post_id):
post = Post.objects.get(id=post_id)
if request.method == 'POST':
post.delete()
return redirect('post-list')
else:
return render(request, 'posts/post_confirm_delete.html', {'post': post})
삭제할 때는 바로 삭제되는 것 보단 한번더 물어보는게 좋다.
처음 요청시는 get방식으로 else로 들어간다.
여기서 템플릿으로 연결해 삭제할 것인지를 다시 물어본다.
확인 후 전송하면 post방식으로 if로 들어가 데이터를 삭제하고 redirect한다.
유효성 검사
유효성 검사는 데이터가 우리가 원하는 규격에 맞는지 확인하는 과정이다.
모델에서 유효성 검증하기
모델 필드와 폼 필드 둘다 유효성 검사를 할 수 있고
모델 폼을 사용하면 모델에 유효성 검사를 추가하면 된다.
Field인자로 유효성 검사하기
class Post(models.Model):
title = models.CharField(max_length=50, unique=True, error_messages={'unique': '이미 있는 제목입니다!'})
blank옵션은 폼에 빈칸(““)을 허용할지 설정한다.
blank=False 가 기본값이다.
null옵션은 데이터에 빈 값(null)을 null로 저장하는 것을 허용할지 설정한다.
unique옵션은 같은 내용사용이 불가능 하다.
unique=False가 기본값이다.
error_messages={‘a’: ‘b’}
a에러가 발생하면 b를 출력한다.
뷰에서 .is_valid()로 유효성 확인을 한다.
validator로 유효성 검증하기
validator는 임의의 값을 받아서 내부의 기준을 충족하지 않으면 ValidationError를 발생시키는 함수다.
- 장고에서 제공하는 validator
from django.core.validators import MinLengthValidator
class Post(models.Model):
content = models.TextField(validators=[MinLengthValidator(10, '10자 이상 작성해주세요!')])
- validator만들어서 사용
from django.core.exceptions import ValidationError
def validate_symbols(value):
if ("@" in value) or ("#" in value):
raise ValidationError("@와 #은 포함될 수 없습니다.", code='symbol-err')
이렇게 validators.py에 작성하고 모델에서 가져다 쓰면 된다.
from django.core.validators import MinLengthValidator
from .validators import validate_symbols
class Post(models.Model):
content = models.TextField(validators=[MinLengthValidator(10, '10자 이상 작성해주세요!'), validate_symbols])
폼에서 유효성 검사하기
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content']
def clean_title(self):
title = self.cleaned_data['title']
if '*' in title:
raise ValidationError('*는 포함될 수 없습니다.')
return title
모든 폼 클래스는 cleaned_data가 있다.
cleaned_data에는 폼 필드를 정의할 때 넣은 유효성 검사를 통과한 데이터가 들어있다. 폼 필드를 사용하지 않았다면 입력한 데이터가 그대로 넘어온다.
폼 템플릿 에러 출력
<form method="post">{% csrf_token %}
{{form.as_ul}}
<input type="submit" value="전송">
</form>
기존에 .as_ul을 사용하면 장고에서 만든 기능이므로 에러메시지 출력도 포함되어 있지만 디자인을 적용하기 위해 변경할 때는 에러메시지를 출력하도록 만들어 줘야한다.
<form method="post">{% csrf_token %}
<h3>제목</h3>
<p>{{form.title}}</p>
{% for error in form.title.errors %}
<p>{{error}}</p>
{% endfor %}
<input type="submit" value="전송">
</form>
이렇게 에러메시지를 출력하면 된다.
에러메시지가 여러개일 수도 있으므로 for문을 사용한다.
Leave a comment