디지털 양피지/Flask2015. 12. 18. 11:48

Web Form


Flask-WTF

Flask-WTF 은 web form을 지원하기 위한 확장(extension) 모듈이다. 

(venv) $ pip install flask-wtf


1. Cross-Site Request Forgery(CSFT) Protection

Flask에서는 CSRF attack을 보호하기 위해 모든 폼(form)에 대한 protection을 기본(default)로 한다. 

Flask-WTF에서는 key encryption을 위하한 구성(configure)을 필요로 한다. 이 키를 이용하여 request data를 encryption하고 인증(authenticity) 한다.


app = Flask(__name__)

app.config['SECRET_KEY'] = 'hard to guess string'

 

app.config는 dictionary 형태의 일반 목적(general-purpose) 저장소 이다. 'SECRET_KEY' configuration은 일반목적의 encryption에 사용되어 지는 것으로 사용 되는 서버 외에는 노출 시키지 않도록 해야 한다.


Form Classes

Flask-WTF에서는 web form이 하나의 class 형태로 지원되며 이 클래스 이름은 Form이다. 


from flask.ext.wtf import Form

from wtforms import StringField, SubmitField

from wtforms.validators import Required


class NameForm(Form):

name = StringField('What is your name?', validators=[Required()])

submit = SubmitField('Submit')


각 class는 form의 field 의 list 형태로 정의 되어지고, 각 필드들은 1개 이상의 validators를 붙일(attached) 수 있다. validator는 사용자에 의해 정확하게 입력값을 제출 되었는지 확인한다. 필드들으니 class의 변수 형태로 저장이 되며 각 클래스 변수들은 field type의 object 로 할당(assigned)되어진다.


StringField는 <input>요소(element)의 속성(attribute) type='text'를 나타내고,

SubmitField는 <input> 요소의 속성 type='submit'을 나타낸다. StringField의 첫번째 param은 HTML에 rendering 할때 label로 사용되어 진다. Required() validator는 submit되었을 때 값이 없는지(empty)인지를 확인한다. 


Flask-WTF에서는 다음과 같은 Field type이 지원된다.

Validator에는 다음과 같은 것들이 지원된다.



2. HTML Rendering of Forms

NameFrom 인스턴스(Instance)가 template에 argument에 form 이라는 이름으로 전달 된다면 다음과 같은 형태가 될 것이다.

<form method = "POST">

{{ form.name.label }}{{ form.name() }}

{{ form.submit() }}

</form>


field의 id 난 class 속성을 사용해서 CSS styles을 적용할 수도 있다.

<form method = "POST">

{{ form.name.label }}{{ form.name(id='my-text-field') }}

{{ form.submit() }}

</form>


Flask-Bootstrap을 사용하면 전체적으로 쉽고 높은 수준의 함수(high-level helper function)를 사용하여 rendering 할 수 있다.

{% import "bootstrap/wtf.html" as wtf %}

{{ wtf.quick_form(form) }}

import 지시어는 일반적인 Python에서 사용법과 같다. quick_form() 함수는 bootstrap styles 을 적용한 rendering을 지원한다.


template/index.html을 다음과 같이 바꿀 수 있다.



3. Form Handling in View Functions

새로운 index function은 다음과 같다.


@app.route('/', method=['GET','POST']

def index():

name = None

form = NameForm()

if form.validate_on_submit():

name = form.name.data

form.name.data = ''

return render_template('index.html', form=form, name=name)


route decorator는 GET과 POST 방식 모드를 URL map에 등록한다. method를 등록하지 않으면 기본(default)는 'GET'만 설정된다. 일반적으로 POST를 사용하는게 편리하다. POST는 form을 submit하는 방식이며 GET은 query string으로 data를 보내 browser의 address bar에서 노출될 수 있지만 POST는 body을 통해 전달 할 수 있다.


validate_on_submit()은 POST로 들어 왔을때 validator가 를 통해 입력 값을 검사하여 True 또는 False 값을 return 한다.


4. Redirect and User Sessions

여기에 사용성에 약간의 문제가 있다. submit으로 이름을 제출한 후 refresh 버튼을 누르면 이전 값을 다시 제출 할 것이냐는 warnning이 뜬다. 이것은 우리가 원하는 동작(action)은 아니다.


POST 요청에 대한 redirect 를 통해 처리할 수 있다. redirect는 GET request를 발행(issues) 하여 page을 표시한다. 이러한 trick을 Post/Redirect/Get pattern이라 한다.


하지만 이 역시 약간의 문제를 가지고 있다. POST를 redirect 할 경우 Application은 name을 저장할 곳을 필요로 하게 된다. (곧 GET으로 redirect 되서 없어 진다.) Application은 request간에 user session을 저장 할 수 있도록 하였다. session은 python dictionary와 같이 쓴다.


index 함수를 user session을 사용하여 다음과 같이 바꿀 수 있다.

from flask import Flask, render_template, session, redirect, url_for


@app.route('/', methods=['GET', 'POST'])

from = NameForm()

if form.validate_on_submit:

session['name'] = form.name.data

return redirect(url_for('index'))

return render_template('index.html', form=form, name=session.get('name'))


name 변수는 session 객체의 'name'으로 request 동안 저장 된다. redirect는 redirect('/')로 간결하게 쓸수도 있지만 redirect(url_for('index'))를 권장한다. url_for는 URL map을 사용한 함수를 이용하여 URLs을 보장(guarantee) 할 수 있으며 route name이 바뀌어도 함수를 사용하므로 자동으로 연결 된다.


url_for는 endpoint name을 param으로 필요로 한다. 기본 적으로(by default) route의 endpoint는 view function의 이름으로 사용된다. session은 session.get을 통하여 접근하여 에러를 줄이도록 하자. 이제 refresh 버튼을 눌러도 경고 창이 뜨지 않는다.


5. Message Flashing


때때로 사용자의 Request가 완료 되었을때 상태(status)를 알려주은 유용하다. 이것은 사용자를 위한 하나의 확인 메세지(confirmation message), 경고(warning) 또는 에러(error)가 될 수도 있다.  일반적인 예로 사용자의 ID나 Password가 틀렸을 경우 상태를 안려주는데 많이 사용한다. 아래는 사용자 이름을 비교하여 상태를 나타낸는 예제이다.


from flask import Flask, render_tempate, session, redirect, url_for, flash


@app.route('/', methods=['GET', 'POST']

def index():

form = NameForm()

if form.validate_on_submit():

old_man = session.get('name')

if old_man is not None and old_man != form.name.data:

flash('Looks like you have changed your name!')

session['name'] = form.name.data

form.name.data = ''

return redirect(url_for('index'))

return render_template('index.html', form=form, name=session.get('name'))


저장된 이름과 submit으로 들어온 이름이 같지 않을경우 flash 함수를 호출하여 상태를 보여주려 한다. flash된 메세지를 보여주기 위해서는 template에 작업을 해야 한다.


{% block content %}

<div class="container">

{% for message in get_flashed_message() %}

<div class="alert alert-warning">

<button type="button" class="close" data-dismiss="alert">&times;</button>

{{ message }}

</div>

{% endfor %}

{% block page_content %}{% endblock %}

</div>

{% endblock %}


아래와 같이 표시되는 메세지는 loop를 사용하여 다중 메세지를 표시 할 수 있도록 하였다. request에 대해 여러 번의 flash가 사용되어 질 수 있으며 이를 대응하기 위함이다. get_flashed_message()를 통해 얻어온 메세지는 다음번에는 값을 리턴하지 않는다. flashed message는 한번만 나타나며(appear) 바로 폐기되어 진다.


Posted by 빨간 양말