簡単な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)
<!-- menu.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ファイルに書き込んで、投稿一覧を表示できるようになりました。