Flask-SQLAlchemyとFlask-WTF その2
前回のSQLAlchemyの基本を抑えたところで、Flask-WTFを使って フォームからSQLAlchemyを動かしてみます。
Flask-WTF
Flask-WTFはValidationやCSRF対策を施したフォームを作成するためのFlask拡張。
Flask-WTFを使ってデータの挿入と削除機能を実装してみる。
まずはインストール
pip install Flask-WTF
前回のtest.pyに
from flask_wtf import FlaskForm
from flask_wtf.csrf import CSRFProtect
from wtforms import StringField, SubmitField, HiddenField
from wtforms.validators import DataRequired,Length
と app = Flask(name)以下にCSRFトークン生成のための秘密鍵
csrf = CSRFProtect(app)
app.config['SECRET_KEY'] = os.urandom(32)
を追加。
次にフォーム用のクラスを作る。
class Delete(FlaskForm):
id = HiddenField('hidden')
submit = SubmitField('delete')
class Insert(FlaskForm):
username = StringField('UserName', validators=[DataRequired()
,Length(max=10,message="名前は10文字以下で")])
job = StringField('Job', validators=[DataRequired()])
submit = SubmitField('追加')
StringFieldはテキストフィールドを表し、HiddenFieldは隠しフィールド。 validators=[DataRequired()] はデータ入力必須項目。Length(max=10)は最大文字数10文字。 各フィールドの最初の引数がフォームをレンダリングする際のラベルの設定。
各種設定はFlask-WTFではなく本家WTFormsのドキュメントをチェックしてください。
テンプレートにフォームを生成
test.pyにルーティングの設定をしてhtmlで表示してみる。
from flask import Flask, request, redirect, url_for, render_template
from flask_sqlalchemy import SQLAlchemy
import os
from flask_wtf import FlaskForm
from flask_wtf.csrf import CSRFProtect
from wtforms import StringField, SubmitField, HiddenField
from wtforms.validators import DataRequired,Length
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///test.db"
# CSRFトークン生成のための秘密鍵
app.config['SECRET_KEY'] = os.urandom(32)
csrf = CSRFProtect(app)
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
job = db.Column(db.String(30), nullable=False)
def __repr__(self):
return '<User %r,%r,%r>' % (self.id,self.username,self.job)
class Delete(FlaskForm):
id = HiddenField('hidden')
submit = SubmitField('delete')
class Insert(FlaskForm):
username = StringField('UserName', validators=[DataRequired()
,Length(max=10,message="名前は10文字以下で")])
job = StringField('Job', validators=[DataRequired()])
submit = SubmitField('追加')
@app.route('/')
def index():
form_d = Delete()
form_i = Insert()
result = User.query.all()
return render_template('test.html',result=result,form_d=form_d,form_i=form_i)
if __name__ == "__main__":
app.run(debug=True)
インスタンスを生成して変数form_d,form_iに代入。User.query.all()でテーブルuserの全リストを取得。
test.html
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Job</th>
<th></th>
</tr>
</thead>
<tbody>
{% for row in result %}
<tr>
<td>{{row.id}}</td>
<td>{{row.username}}</td>
<td>{{row.job}}</td>
<td>
<form method="POST" action="{{url_for('index')}}">
{{ form_d.csrf_token }}
{{form_d.id(value=row.id)}}
{{form_d.submit}}
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p>
<form method="POST" action="{{url_for('index')}}">
{{form_i.csrf_token}}
{{form_i.username.label}}:{{form_i.username(size=20)}} {{form_i.job.label}}:{{form_i.job}} {{form_i.submit}}
</form>
</p>
{{ 変数.csrf_token }}がCSRFトークンの隠しフィールドを生成。
その他のフィールドも変数.~~
で生成されるので楽ちん。
とりあえずなんの装飾も施してないページができました。
レコード削除とレコード追加機能を実装
@app.route('/', methods=['GET', 'POST'])
def index():
form_d = Delete()
form_i = Insert()
# insert処理
if form_i.validate_on_submit():
t = User(username=form_i.username.data, job=form_i.job.data)
db.session.add(t)
db.session.commit()
return redirect(url_for('index'))
# delete処理
elif form_d.id.data:
user = User.query.get(form_d.id.data)
db.session.delete(user)
db.session.commit()
return redirect(url_for('index'))
# それ以外
result = User.query.all()
return render_template('test.html',result=result,form_d=form_d,form_i=form_i)
validate_on_submit()
がPOSTリクエストであるかどうか、そしてそれが有効であるかどうかをチェックしているので、if request.method == 'POST':
は書かなくていいみたい。
フォームのデータはオブジェクト.data
で受け取れます。
10文字以上のnameで登録するとエラーメッセージが出るようにテンプレートに以下を追加
{% if form_i.username.errors %}
{% for message in form_i.username.errors %}
{{message}}
{% endfor %}
{% endif %}
実際に動かしてみる
insert
空白でボタンを押すと
delete
10文字以上の名前を登録すると
更新
class Update(FlaskForm):
id = HiddenField('hidden')
username = StringField('username', validators=[
DataRequired(), Length(max=10, message="名前は10文字以下で")])
job = StringField('job', validators=[DataRequired()])
submit = SubmitField('update')
@app.route('/', methods=['GET', 'POST'])
def index():
form = Update()
if form.validate_on_submit():
r = User.query.get(form.id.data)
r.username = form.username.data
r.job = form.job.data
db.session.commit()
return redirect(url_for('index'))
result = User.query.all()
return render_template('test.html', result=result, form=form)
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Job</th>
<th></th>
</tr>
</thead>
<tbody>
{% for row in result %}
<form method="POST" action="{{url_for('index')}}">
{{form.csrf_token}}
<tr>
<td>{{row.id}}</td>
<td>{{form.username(value=row.username)}}</td>
<td>{{form.job(value=row.job)}}</td>
<td>
{{form.id(value=row.id)}}
{{form.submit}}
</td>
</tr>
</form>
{% endfor %}
</tbody>
</table>
ゴチャゴチャしてきたので更新機能だけのページに変更。
見た目変化ないけど更新成功しています。