[점프 투 플라스크] 질문등록 & 폼모듈
폼 모듈 설치; pip install flask-wtf
CSRF토큰 생성; CSRF공격을 막기위한 토큰. 폼으로 전송된 데이터가 실제 웹페이지에서 작성된 데이터인지 판별
config.py에 SECRET_KEY 변수 추가.
질문 등록
질문 등록 버튼 만들기
question_list.html 일부 |
<a href="{{ url_for('question.create') }}" class="btn btn-primary">질문 등록하기</a> |
질문,답변 폼 만들기
forms.py 전체 |
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField
from wtforms.validators import DataRequired
class QuestionForm(FlaskForm):
subject = StringField('제목', validators=[DataRequired('제목은 필수입력 항목입니다')])
content = TextAreaField('내용', validators=[DataRequired('내용은 필수입력 항목입니다')])
class AnswerForm(FlaskForm):
content = TextAreaField('내용', validators=[DataRequired('내용은 필수입력 항목입니다.')])
|
제목은 StringField(글자수제한), 본문은 TextAreaField(글자수제한x)
각각의 첫번째 인자(입력인수)는 폼 라벨이다. 이 라벨을 통해 "제목"이라는 라벨을 출력 가능.( {{form.subject.label}} )
각각의 두번째 인자(입력인수)는 validators다. validators는 검증을위해 사용되는 도구로, DataRequired(필수항목인지),Email(이메일 형식인지) 등이 있다.
질문 등록 라우팅 함수
question_views.py 일부 |
@bp.route('/create/', methods=('GET', 'POST'))
def create():
form = QuestionForm()
if request.method == 'POST' and form.validate_on_submit():
question = Question(subject=form.subject.data, content=form.content.data, create_date=datetime.now())
db.session.add(question)
db.session.commit()
return redirect(url_for('main.index'))
return render_template('question/question_form.html', form=form)
|
1. 질문 목록에서 "질문 등록"버튼을 눌러 create함수 GET방식으로 호출.
2. create에서 Question form과함께 question_form.html로 렌더링
4. create함수에서 POST방식으로 받아 if문의 request.method='POST'가 참이되고(form엘리먼트로 전송된 데이터는 request객체로 얻을 수 있음) 전송받은 데이터의 정합성(DataRequired 등)을 form.validate_on_submit()으로 점검한다.
5. 조건문이 참이므로 데이터베이스에 작성한 Question을 추가하고, main을 별칭으로 하는 모듈의 index라우팅함수의 주소로 이동(리다이렉트)한다.
method 속성 추가
GET방식과 POST방식 모두 사용할 것 이므로 라우팅 애너테이션에 methods속성 GET, POST 추가.
전달받은 폼 데이터 저장
if문 안에 form이 있는데 여기서 폼은 form=QuestionForm()의 form이 아니라 question_form.html에서 전달받은 form임. 원래는 form=QuestionForm()의 form이였는데 전달받으면서 갱신,변경됐다고 생각하면 될 듯.
뒤에 공부하다보니 form이 또 나오는데 위 설명 틀린듯.
form은 QuestionForm()맞고, form.validate_on_submit()은 submit된 데이터가 QuestionForm()의 형식에 맞게 작성되었는가를 확인하는 듯.
form.validate_on_submit()이 전송받은 데이터의 정합성을 점검하기 때문에 일단 데이터가 전송돼야 한다는 뜻.
subject=form.subject.data << 이 코드를 보면 form이 전달받은 데이터임을 알 수 있음. 전달받은 데이터의 subject의 data를 할당하는 모습
질문등록 템플릿
question_form.html |
{% extends 'base.html' %}
{% block content %}
<!-- 질문 등록 -->
<div class="container">
<h5 class="my-3 border-bottom pb-2">질문등록</h5>
<form method="post" class="my-3">
<!-- 오류표시 Start -->
{% if form.errors %}
<div class="alert alert-danger" role="alert">
{% for field, errors in form.errors.items() %}
<strong>{{ form[field].label }}</strong>
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endfor %}
</div>
{% endif %}
<!-- 오류표시 End -->
{{ form.csrf_token }}
<div class="mb-3">
<label for="subject">제목</label>
<input type="text" class="form-control" name="subject" id="subject"
value="{{ form.subject.data or '' }}">
</div>
<div class="mb-3">
<label for="content">내용</label>
<textarea class="form-control" name="content" id="content" rows="10">{{ form.content.data or '' }}</textarea>
</div>
<button type="submit" class="btn btn-primary">저장하기</button>
</form>
</div>
{% endblock %}
|
3. 사용자가 제목과 내용을 넣고 저장하기 버튼을 누르면 POST방식으로 채운 내용(Question form에 맞게 작성한 데이터)과 함께 현재있는 주소(form에 action속성이 지정되지 않았기 때문에 현재 페이지 URL이 디폴트 action으로 설정됨)에 해당하는 라우팅함수 호출
form엘리먼트에서 action 속성을 지정하지 않은 이유
이 템플릿을 "질문수정"할 때도 사용할 것이기 때문임.
action 속성을 url_for('question.create')로 지정하게되면 create라우팅 함수로만 연결돼서 question_form.html을 질문등록에서만 사용 가능하다.
이렇게 하면 현재페이지의 url로 action값이 default로 저장돼서 현재페이지의 url로 form이 자동으로 전달됨
폼 전달 방법
<lable>태그를 통해 subject의 레이블이라는 의미를 부여하면서, 제목이라는 텍스트를 화면에 띄움.
<input>태그를 통해 text타입, form-control이라는 클래스(부트스트랩 클래스)로 입력한 내용을 form.subject의 형태로 라우팅 함수에 전달. 이때 value를 설정한 이유는, 폼을 전송했을 때(질문 등록했을 때) 오류가 있더라도 입력한 값을 유지하도록 하기 위함. 폼 전송하면 form.subject.data << 여기에 담기는데 오류떠도 value={{ form.subject.data }}에 입력한 값이 유지됨. or ' '은 GET방식으로 처음 템플릿이 요청되는 경우 빈칸으로 출력하기 위함임.
내용도 마찬가지. <input>태그랑 <textarea>태그 형식 다른거 주의.
오류 처리
form.validate_on_submit()가 false를 반환하게되면 form.errors에 오류내용들이 자동으로 등록된다.
따라서 반복문을 보면 form.errors.items()는 에러가 발생한 item(입력 폼의 필드(subject, content))각각에서 필드와 에러들을 filed, errors에 저장함. 이후, 다시 각각의 필드에 대해 모든 에러들을 출력.
CRSF 토큰 오류
템플릿 모듈의 <form>시작태그 바로 밑에 {{ form.csrf_token }}을 추가해줘야 질문 등록이 가능함.(실제 웹사이트에서 만들어진 데이터인지 검증)
답변폼, 답변등록, 답변라우팅 함수
질문과 그 논리가 같아 생략