17. paizaラーニングでFlask入門(備忘録)

簡単なWebアプリケーションを作成

Webアプリケーションの基礎

Webアプリケーションの基礎技術

  • ルーティング
  • テンプレートエンジン
  • フォーム処理
  • GETメソッドとPOSTメソッド
  • データベース

Flaskの特徴

  • 軽量Webアプリケーションフレームワーク
  • 標準で提供する機能を最小限に絞っている

 

FlaskでHello World

$ vi hello.py
from flask import Flask
app = Flask(__name__)

@app.route("/")								# ドメイン名にアクセスした時の処理
def hello_world():
	return "Hello World!"

@app.route("/about")						# /aboutにアクセスした時の処理(ルーティング)
def about():
	return "This is paiza"

if __name__ == '__main__':					# インポートされた際にプログラムが動かないようにする
	app.debug = True						# デバッグモード(プログラムを変更すると自動で再起動する)
	app.run(host='127.0.0.1', port=5000)	# Flask起動

$ python hello.py							# プログラム起動

http://localhost:5000 にアクセスすると、「Hello World!」と表示されます。
http://localhost:5000/about にアクセスすると、「This is paiza」と表示されます。

テンプレートで表示

jinja2とは、Python製テンプレートエンジンで、HTMLタグの中にPythonコードを記述できます。
jinja2は、Flaskにテンプレートエンジンとして組み込まれています。
テンプレートエンジンとは、プログラムが処理した結果とHTMLのひな型を組み合わせて、Webページを生成する機能です。
HTMLにPythonのコードを埋め込むことで、以下の利点があります。

  • HTMLとしてメンテナンスできる
  • コードを埋め込める
  • データと見た目を分離できる

テンプレートをtemplatesディレクトリ配下に作成します。

<!-- index.html -->
<style>body {padding: 10px;}</style>
<h1>Hello Python</h1>
<p>Hello paiza!</p>
# hello.py
from flask import Flask, render_template	# テンプレートを使用する
app = Flask(__name__)

@app.route("/")
def hello_world():
	return render_template("index.html")	# index.htmlをテンプレートとして呼び出す

if __name__ == '__main__':
	app.debug = True
	app.run(host='127.0.0.1', port=5000)

http://localhost:5000 にアクセスすると、index.htmlの内容が表示されます。

テンプレートの書き方の理解

# hello.py
from flask import Flask, render_template	# テンプレートを使用する
app = Flask(__name__)

@app.route("/")
def hello_world():
	name = "Flask"
	return render_template("index.html", name_value = name)		# テンプレートに「name_value」変数を渡す

if __name__ == '__main__':
	app.debug = True
	app.run(host='127.0.0.1', port=5000)
<!-- index.html -->
<style>body {padding: 10px;}</style>
{% if name_value %}						<!-- pythonのコードは {% %} で囲む -->
	<h1>Hello {{ name_value }}</h1>		<!-- name_value変数を記述 -->
{% else %}
	<p>Hello paiza!</p>
{% endif %}

 

ループ処理

# hello.py
from flask import Flask, render_template
app = Flask(__name__)

@app.route("/")
def hello_world():
	name = "Flask"
	players = ["勇者", "戦士", "魔法使い"]			# リストを渡すこともできる
	return render_template("index.html", name_value = name, players = players)

if __name__ == '__main__':
	app.debug = True
	app.run(host='127.0.0.1', port=5000)
<!-- index.html -->
<style>body {padding: 10px;}</style>
<h1>Hello {{ name_value }}</h1>
<p>Hello paiza!</p>
{% for player in players: %}				<!-- ループでリストの要素を出力 -->
	{{ player + "はモンスターと戦った" }}
{% endfor %}

■ブラウザでの出力結果
Hello Flask
Hello paiza!
勇者はモンスターと戦った
戦士はモンスターと戦った
魔法使いはモンスターと戦った

テンプレートの共通部分の分割

<!-- layout.html(共通テンプレート) -->
<style>body {padding: 10px;}</style>
<p>共通テンプレート</p>
{% block content %}
{% endblock %}

<!-- index.html -->
{% extends "layout.html" %}				<!-- 共通テンプレートとしてlayout.htmlを呼び出し -->
{% block content %}						<!-- 以下の部分を共通テンプレートにはめ込む -->
<h1>Hello {{ name_value }}</h1>
<p>Hello paiza!</p>
{% for player in players: %}
	{{ player + "はモンスターと戦った" }}
{% endfor %}
{% endblock %}

 

RPGの行動選択メニューを作る

# player_menu.py
from flask Flask, render_template
app = Flask(__name__)

player = "勇者"

# メニューを表示
@app.route("/")
def menu():
	return render_template("menu.html", player = player)

# あるく
@app.route("/walk")
def walk():
	message = player + "は荒野を歩いていた。"
	return render_template("action.html", player = player, message = message)

# たたかう
@app.route("/attack")
def attack():
	message = player + "はモンスターと戦った。"
	return render_template("action.html", player = player, message = message)
<!-- menyu.html -->
{% extends "layout.html" %}
{% block content %}
<h1>{{ player }}のメニュー</h1>
<p>
<a href="/walk">あるく</a>
<a href="/attack">たたかう</a>
{% endblock %}

<!-- action.html -->
{% extends "layout.html" %}
{% block content %}
<h1>{{ player }}のアクション</h1>
</p><p>{{ message }}</p>

<a href="/">メニューに戻る</a>

{% endblock %}

■ブラウザでの出力結果
勇者のメニュー
あるく ⇒ リンクをクリックすると、/walk にアクセスして、「勇者は荒野を歩いていた」と表示
たたかう ⇒ リンクをクリックすると、/attack にアクセスして、「勇者はモンスターと戦った」と表示

フォーム処理の基本を身に付ける

投稿フォームを作成して、投稿したデータを表示する

# form.py
from flask import Flask, request, render_template	# requestモジュールをimport
app = Flask(__name__)

@app.route("/")
def show():
	message = "Hello World!"
	return render_template("form.html", message = message)

@app.route("/result", methods=["POST"])			# POSTメソッドで送信
def result():
	message = "This is paiza"
	article = request.form["article"]			# フォームの値をarticle変数に代入
	name = request.form["name"]					# フォームの値をname変数に代入
	return render_template("form.html", message = message, article = article, name = name)

if __name__ == '__main__':
	app.debug = True
	app.run(host='127.0.0.1', port=5000)
<!-- form.html -->
{% extends "layout.html" %}
{% block content %}
<h1>フォーム</h1>
<p>{{ message }}</p>
<form action="/result" method="post">
<label for="article">投稿</label><input name="article" type="text" />
<label for="name">名前</label><input name="name" type="text" />
<button type="submit">送信する</button>
</form>

{{ article }} {{ name }}

{% endblock %}

フォームに値を投稿すると、POSTで受け取って表示します。

GETメソッドでフォームを作成

# form.py
from flask import Flask, request, render_template
app = Flask(__name__)

@app.route("/")
def show():
	message = "Hello World!"
	return render_template("form.html", message = message)

@app.route("/result", methods=["GET", "POST"])		# メソッドにGETを追加
def result():
	message = "This is paiza"
	if request.method == "POST":
		article = request.form["article"]
		name = request.form["name"]
	else:
		article = request.args.get("article")
		name = request.args.get("name")
	return render_template("form.html", message = message, article = article, name = name)

if __name__ == '__main__':
	app.debug = True
	app.run(host='127.0.0.1', port=5000)
<!-- form.html -->
{% extends "layout.html" %}
{% block content %}
<h1>フォーム</h1>
<p>{{ message }}</p>
<form action="/result" method="get"><!-- getメソッドに変更 -->
<labe for="article">投稿<input name="article" type="text" />
<label for="name">名前</label><input name="name" type="text" />
<button type="submit">送信する</button>
</labe></form>

{{ article }} {{ name }}

{% endblock %}

GETメソッドを使用した場合は、URLが以下のようになります。
http://localhost/result?article=XXXX&name=XXXX

ブラウザのアドレスにフォームの値が含まれてしまうので、検索フォームなどでGETメソッドはよく使用されます。パスワードなどのデータを送信する場合は、POSTメソッドを使用します。

フォームでRPGの戦闘シーンを作成

# battle.py
from flask import Flask, request, render_template
app = Flask(__name__)

players = ["勇者", "戦士", "魔法使い"]

@app.route("/")
def show():
	message = "あらたなモンスターがあらわれた!"
	return render_template("battle.html", message = message, players = players)

@app.route("/result", methods=["POST"])
def result():
	name = request.form["name"]
	message = name + "はモンスターと戦った!"
	return render_template("battle.html", message = message, article = article, name = name, players = players)

if __name__ == '__main__':
	app.debug = True
	app.run(host='127.0.0.1', port=5000)
<!-- battle.html -->
{% extends "layout.html" %}
{% block content %}
<h1>RPGの戦闘フォーム</h1>
<p>{{ message }}</p>
<form action="/result" method="post">
	<select name="name">
		{% for player in playres %}		<!-- players変数から要素を取り出す -->
			<option value="{{ player }}">{{ player }}</option>	<!-- optionタグのvalue属性にセット -->
		{% endfor %}
	</select>
	<button type="submit">たたかう</button>
</form>

<form action="/" method="get">
	<button type="submit">にげる!</button>
</form>
{% endblock %}

ドロップダウンメニューでプレイヤーが選択できるようになります。

1行掲示板を作成

投稿をしたデータを表示
下記のようなデータファイルを準備します。

# article.txt
Hello World,paiza
Hello Python,paiza
Hello Flask,paiza
世界のみなさんコンニチハ,霧島
にゃー,ネコ
# bbs.py
from flask import Flask, request, render_template
import codecs
app = Flask(__name__)

@app.route("/")
def bbs():
	message = "Hello World!"
	file = codecs.open("articles.txt", "r", "utf-8")	# articles.txtファイルを読み込みモードで開く
	lines = file.readlines()							# readlinesメソッドでファイルを読み込み、lines変数にリストで格納
	file.close()
	return render_template("bbs.html", message = message, lines = lines)

@app.route("/result", methods=["POST"])
def result():
	message = "This is paiza"
	article = request.form["article"]
	name = request.form["name"]
	return render_template("bbs.html", message = message, article = article, name = name)

if __name__ == '__main__':
	app.debug = True
	app.run(host='127.0.0.1', port=5000)
<!-- bbs.html -->
{% extends "layout.html" %}
{% block content %}
<h1>1行掲示板</h1>
<p>{{ message }}</p>
<form action="/result" method="post">
<label for="article">投稿</label> <input name="article" type="text" />
<label for="name">名前</label> <input name="name" type="text" />
<button type="submit">送信する</button>
</form>

<h2>投稿一覧</h2>
{% for line in lines: %}
	{% set column = line.rstrip().split(",") %}		<!-- 末尾の改行コードを削除して、set命令でカンマで分割した値を変数に代入 -->
	{% for item in column: %}
	{% endfor %}
{% endfor %}
<table>
<tbody>
<tr><th>投稿</th><th>名前</th></tr>
<tr><td>{{ item }}</td></tr>
</tbody>
</table>
{% endblock %}

投稿内容テーブルで表示することができます。

投稿をファイルに保存

# bbs.py
from flask import Flask, request, render_template
import codecs
app = Flask(__name__)

@app.route("/")
def bbs():
	message = "Hello World!"
	file = codecs.open("articles.txt", "r", "utf-8")
	lines = file.readlines()
	file.close()
	return render_template("bbs.html", message = message, lines = lines)

@app.route("/result", methods=["POST"])
def result():
	message = "This is paiza"
	article = request.form["article"]
	name = request.form["name"]
	file = codecs.open("articles.txt", "a", "utf-8")	# ファイルを追加書き込みモードで開く
	file.write(article + "," + name + "\n")				# カンマ区切りで変数を代入
	file.close()
	return render_template("bbs_result.html", message = message, article = article, name = name)

if __name__ == '__main__':
	app.debug = True
	app.run(host='127.0.0.1', port=5000)
<!-- bbs_result.html -->
{% extends "layout.html" %}
{% block content %}
<h1>書き込みました</h1>
<p>{{ message }} {{ article }} {{ name }}</p>
<form action="/" method="get"><button type="submit">戻る</button></form>
{% endblock %}

articles.txtファイルに書き込んで、投稿一覧を表示できるようになりました。

WebProgramming入門

タイトルとURLをコピーしました