-
장고 Forms를 활용한 효율적인 데이터 유효성 검사 및 저장카테고리 없음 2021. 11. 1. 13:58
<HTML Form>
HTML Form (클라이언트 측) : 클라이언트에서 사용자에게 입력폼을 제공하고, 이를 서버로 전송하고자 할 때.
Django Form (서버 측) : 클라이언트로부터 전달받은 값들에 대한 유효성 검사를 수행하고, 이를 데이터베이스에 저장하는 등의 처리
<form action="" method="POST">
<input type="text" /> : 1줄 문자열 입력
<textarea></textarea> : 1줄 이상의 문자열 이비력
<select></select> : 항목 중 택일
<input type="checkbox"/> : 체크박스
<input type="radio"/> : 라디오박스
</form>
<HTML <form> 태그 필수 속성 >
1. action : 요청을 보낼 주소
2. method : 전송 방식
- "GET": 주로 데이터 조회 요청 시에 사용
- "POST" : 파괴적인 액션(생성/삭제/수정)에서 사용
3. enctype : 인코딩 방식
- GET 요청에서는 한 enctype으로 강제됨.
- POST 요청에서만 유효. 파일등 다양한 소스를 넣는다면 enctype = "multipart/form-data"
<Form 요청에서 인자 보내는 2가지 방법>
1) 요청 URL 뒤에 ?를 붙이고, 인자를 실어서 보내기
- Query String 인자라고 부른다.
2) 요청 body에 모든 인코딩의 인자를 실어서 보내기
<2가지 Form Method>
GET 방식
- 엽서에 비유, 물건을 보낼 수 없다.
- application/x-www-form-urlencoded 만이 강제되며, 인코딩된 문자열을 QueryString으로 전달
POST 방식
- 택배에 비유. 다양한 물건을 보낼 수 있다.
- 다양한 인코딩을 모두 사용 가능하며, 인코딩된 데이터를 "요청 Body"에 담아서 전달
# post에 파일 업로드를 수행한다면, enctype = "multipart/form-data" 를 꼭 넣어주어야 한다.
<HttpRequest(요청)와 HttpResponse(응답)>
HttpRequest 객체
클라이언트로부터의 모든 요청 내용을 담고 있으며,
- 함수 기반 뷰: 매 요청 시마다 뷰 함수의 첫번째 인자 request로 전달
- 클래스 기반 뷰: 매 요청 시마다 self.request를 통해 접근
Form 처리 관련 속성들
.method : 요청의 종류 "GET" or "POST"
.GET : GET 인자 목록 (QueryDict 타입. key 중복 허용)
.POST : POST 인자 목록 (QueryDict 타입. key 중복 허용)
.FILES : POST 인자 중에서 파일 목록
MultiValueDict
- dict를 상속받은 클래스. 동일 key의 다수 value를 지원하는 사전
- 수정 불가능한 특성
<DjangoForm>
장고를 사용함에 있어서 가장 중요한 것은 Form
Model: 데이터베이스와의 interaction
Forms: User의 입력폼과의 interaction
<Django Style의 Form 처리>
하나의 URL (하나의 View)에서 2가지 역할을 모두 수행
1) 빈 폼을 보여주는 역할
2) 폼을 통해 입력된 값을 검증하고 저장하는 역할
GET 방식으로 요청 받았을 때
- New/Edit 입력폼을 보여줍니다.
POST 방식으로 요청 받았을 때
- 데이터를 입력받아 유효성 검증 수행
- 검증 성공 시 : 해당 데이터를 저장하고 SUCCESS URL로 이동
- 검증 실패 시 : 오류메세지와 함께 입력폼을 다시 보여줍니다.
<form을 사용한 views.py 예시>
if request.method == 'POST': # POST 요청일 때 form = PostForm(request.POST, request.FILES) else: # GET 요청일 때 form = PostForm()
<modelForm을 사용할 때, forms.py>
class PostForm (forms.ModelForm): class Meta: model = Post fields = ["필요한 것들..."] or "__all__"
<Cross Siste Request Forgery (CSRF) = 사이트 간 요청 위조 공격>
사용자가 의도하지 않게 게시판에 글을 작성하거나, 쇼핑을 하게 하는 등의 공격
공격을 막기 위해 Token을 통한 체크
- POST 요청에 하해 CsrfViewMiddleware를 통한 체크 => settings의 middleware에 있음
forms.py에서 POST 요청일 때, {% csrf_token %} 하나만으로 체크가 가능하다
csrf_token은 현재 인증이 유효한지를 판단
따라서, 유저인증 Token, JWT와는 다르다!
<ModelForm>
Form / ModelForm
<Form을 사용할 때>
class PostForm(forms.Form):
title = forms.CharField()
content = forms.CharField(widget=forms.Textarea)
<ModelForm을 사용할 때>
class PostModelForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content']
=> 주로 ModelForm을 사용하자! 하지만, model에서 받아올 것이 없는 form을 만들 때만 그냥 Form을 사용한다.
save를 함에 있어서 default는 (commit=True) 이다. 보통은 이렇게 많이 쓰지만, form에서 세부 정보를 수정해야 할때는, commit = False로 받고 나머지를 수정한다음 save() 하면 된다.
forms.py에서
fields = [이것을 포함하겠다] ex) '__all__'도 있음
exclude = [이것을 배제하겠다]
편집과정에서는 instance를 입력해 주면 된다.
ex). form = PostModelForm(request.POST, request.FILES, instance=post)
<Form Validation>
Form 유효성 검사가 수행되는 시점
- form을 지정한 후, 바로 유효성 검사를 시작하고, 모든 유효성(fields)이 통과되면 참으로 반환한다.
유효성 검사 호출로직 (form.is_valid()호출 당시)
1. form.full_clean() 호출
1) 각 필드 객체 별로
- 각 필드캑체.clean() 호출을 통해 각 필드 Type에 맞춰 유효성 검사
2) Form 객체 내에서 (forms.py)
- 필드 이름 별로 Form객체.clean_필드명() 함수가 있다면 호출해서 유효성 검사 => 1개 일 때
- Form객체.clean() 함수가 있다면 호출해서 유효성 검사 => 여러개 일 때
2. 에러 유무에 따른 Tue/False 리턴
Form에서 수행하는 2가지 유효성 검사
1. Validator 함수를 통한 유효성 검사
2. Form 클래스 내 clean, clean_ 멤버함수를 통한 유효성 검사 및 값 변경
언제 validators를 쓰고, 언제 clean을 작성?
- 가급적이면 모든 validators는 모델에 정의하고, ModelForm을 통해 모델의 validators 정보도 같이 가져오기
- clean은 값을 변경할 수 있지만, validators는 값 체크기능만 존재한다.
clean이 필요할 때
- 특정 Form에서 1회성 유효성 검사 루틴이 필여할 때
- 다수 필드값에 걸쳐서, 유효성 검사가 필요할 때
- 필드 값을 변경할 필요가 있을 때
<Messages Framework>
Messages : admin에서 특정한 action을 처리했을 때, alert 뜨는 것. ex) "저장했습니다", "로그인 되었습니다"
Message Levels를 통한 메시지 분류
레벨의 종류
- DEBUG : 디폴트 설정으로 무시되는 레벨
- INFO : 해당 유저에 대한 정보성 메세지
- SUCCESS : 액션이 성공적으로 수행되었음을 알림
- WARNING : 실패가 아직 발생하진 않았지만, 임박했다.
- ERROR : 액션이 수행되지 않았거나, 다른 Failure가 발생했다.
messages 등록 코드
views.py의 form.is_valid() 안에
messages.add_message(request, messages.SUCCESS, '새 글이 등록되었습니다.') 또는
messages.success(request, '새 글이 등록되었습니다.')