前回までのシリーズ記事
- Flaskによるwebアプリ開発①~簡単なページを表示させる
- flaskでのWebアプリ開発②~テンプレートエンジン(Jinja2)を使って動的Webページを作る
- flaskでのWebアプリ開発③~GETとPOSTとformタグでのデータの送受信
- flaskでのWebアプリ開発④~SQLAlchemyを使ってデータベースと連動させる
今回は少し高度なURLルーティング、つまり変数を使った動的なURLの作成方法について解説していきます。
ルーティングとは何か?
Flaskの使い方を色々書いていて今さらにはなりますが、改めてルーティングについて解説するとWebアプリケーションにおけるルーティングとは、URLと内部処理を紐づけすることです
flaskアプリケーションは、関数とURLを紐付けることで、ユーザからアクセスできるようになっています。flaskのルーティングは、.route()
で定義されます。これまでなんとなくコピペしていたであろう@app.route()
はルーティングを定義しています。
from flask import Flask app = Flask(__name__) # http://xxx 以降のURLパスを '/' と指定 @app.route('/') def index(): return 'hello world!!'
上記の例では、 http://xxx/ と index() を紐付けています。http://xxx/ にアクセスすると、「HelloWorld!!」とだけ書かれた質素なページが表示されます。これがもっともシンプルなルーティングです。
静的ファイルのルーティング
webサイトに必要なのはテキストだけではありません。現在のWebサイトのようなオシャレなものを作る場合はタグの装飾を凝ったり画像や動きを付ける必要があります。
なので、もし個人のお遊びではなく、実務などでまともなWebサイトを作ろうとなるとhtml以外に、画像ファイルや、htmlをオシャレにできる「css」、htmlに動きを付ける「Javascript」を扱う必要があります。
CSSやJavaScript、画像といった静的ファイルのルーティングは自動でされます。デフォルトでは、 http://xxx/static が static ディレクトリに対応しています。
htmlを保管している「templates」と同じように、Flaskのアプリケーションファイル内に 「static」 という名前のディレクトリを作成すると自動的にルーティングされます。
前述したような app.route()
による定義は必要ありません。フォルダのディレクトリ構成は↓のようなイメージになっています。
<FLASKアプリケーションの典型的なディレクトリ構成> Flask app └──app.py │ └──templates │ └──index.html │ └── static │ ├── css │ └── test.css ├── js │ └── test.js └──img └──test.png
static以下のディレクトリは、ディレクトリ名がパスとしてルーティングされます。上記構成の場合、以下のURLが有効です。
http://xxx/static/img/test.png
http://xxx/static/css/test.css
http://xxx/static/js/test.js
httpメソッドまたはif文での条件分岐ルーティング
app.route()
は、引数のmethods=
の部分にHTTPのメソッド名のリストを渡してやることで、そのURLが受け入れるHTTPメソッドを指定できます。
何も指定しない場合のデフォルトはmethods=['GET', 'POST']
で両方とも受け付けます。
例えばユーザが入力フォームに書いた内容を送信する処理などは、methods=['POST']
にすることで、post以外のメソッドを使ったアクセスはじくことができ、セキュリティ対策になります。またメソッドに応じて条件分岐させることも可能です。
@app.route('/', methods=['GET', 'POST']) def edit(): # 共通の処理 if request.method == 'get': # GET時の処理 else: # POST時の処理 # 共通の処理
HTTPメソッドの指定を変えることで、同じURLでも異なる関数へとルーティングできます。
@app.route('/edit', methods=['GET']) def get_edit(): # GET時の処理 @app.route('/edit', methods=['POST']) def post_edit(): # POST時の処理
methods に値を指定することで、HTTPメソッドを指定できます。例では、GETとPOSTのリクエストをルーティングしています。
処理の内容にもよりますが、GETとPOSTで異なる処理をすることがほとんどであり、ifで分岐させるよりも、関数を呼び出す時点で分岐しておいた方がコードが見やすくなるので個人的にはそうやって書くことが多いです。
HTTPメソッドごとに関数を定義する場合は、関数名をget_〇〇、post_〇〇のようにしておくとわかりやすいです。
まあこのcssや条件分岐辺りはコードを見ればなんとなくで理解できるので、今まであまり触れていませんでしたが、Webアプリケーションを構築する上で少し難しいのが「変数による動的なルーティング」をするケースです。
「変数による動的URLなルーティング」の顕著な例としては、ログイン制のサイトがあります。適当なサイトにブラウザからログインして、ブラウザの上部を見てもらえれば分かりますが、ユーザー名ごとに自動でURLが作成され、会員ごとに別々のページが表示されています。
この「変数による動的URLルーティング」は僕がflaskを勉強した時にハマったところなので、今回ここで1つ記事にして解説しようと思った次第です。
変数による動的なURLルーティング
たとえば、会員登録して使うサイトなどではUser ID=1のデータをリクエストと一緒に送信したいという場合があります。その場合、値をURLの一部として渡す、もしくは、クエリとして渡すのが一般的です。
<app.py>
from flask import Flask, render_template, url_for, redirect app = Flask(__name__) # 社員情報を表示する画面 @app.route("/members") def user_url(): lists = [[1,'太郎'], [2,'次郎'], [3,'三郎']] return render_template("member.html", lists=lists) # 社員の詳細情報を表示する画面 @app.route("/member_detail/<int:id_>/<name>") def detail(id_,name): lists = [['太郎',24,'サッカー'], ['次郎',45,'野球'], ['三郎',67,'バスケ']] index = id_-1 data = lists[index] return render_template("detail.html", data=data, name=name) # トップ画面にリダイレクト @app.route('/redirects') def redirects(): return redirect(url_for('index')) app.run(debug=True)
<html> <h2>Flaskでurlforでのルーティングを勉強するサンプルアプリ</h2> <p> ・<a href='/members'>社員情報一覧</a> </html>
<menber.html>
<!DOCTYPE html> <html lang="ja"> <head> <body> <p></p> <table> <tr> <th>社員ID</th> <th>社員名</th> </tr> <tbody> {% block body %} {% for d in lists %} <tr> <td>{{ d[0] }}</td> <td>{{ d[1] }}</td> <td> <a href="{{ url_for('detail',id_=d[0],name=d[1]) }}">詳細</a> </td> </tr> {% endfor %} {% endblock %} </tbody> </table> <p></p> <a href='/redirects'>トップ画面にリダイレクト</a> </body> </html>
<detail.html>
<html> <p></p> <h3>{{ name }}の社員情報です。</h3> <p></p> <table> <tr> <th>年齢</th> <th>趣味</th> </tr> <tbody> <tr> <td>{{ data[1] }}歳</td> <td>{{ data[2] }}</td> </tr> </tbody> </table> <p></p> <a href='/redirects'>トップ画面にリダイレクト</a> </html>
前者のように http://xxx/edit/1 のようにデータをurlに渡したいという場合は、変数付きのルーティングを用います。flaskではURLの後にカッコ有りの記述を入れることで、URLと一緒についてきたものを拾えます。
@app.route("/member_detail/<int:id>/<name>/")
の部分ではidとnameを拾っています。int:と付け足すことでidの中身はint型以外受け付けないと指定しています。
データ型は特に指定されていない場合、string(文字型)として扱われます。converterに指定した型と異なる型のデータがURLとして渡された場合、 404 NotFound を返します。
※デコレーターのURL型の説明
・string (デフォルト) /(半角スラッシュ)以外の全ての文字を受け付けます。
・int 正の整数を受け付けます。
・float 正の実数を整数または、小数を受け付けます。
・path スラッシュを含む全ての文字を受け付けます。
・uuid UUID文字列を受け付けます。
そしてhtml上に既に別のルーティングで渡されている情報を、別のURLに移動する際にくっつけておきたい場合は「url_for」を用います。url_forの引数の指定方法はurl_for('送信先の関数名',送信するデータ)
です。
今回は "{{ url_for('detail',id_=d[0],name=d[1]) }}"
で、送信先の関数としてdetail()を指定しているため、それと紐づいている@app.route("/member_detail//")
にアクセスするURLが生成されます。
今回の動的URLには変数部分にid_とnameが設定されているため、キーワード引数としてidとnameに送るデータを指定します。
今回送信されているのはd[0]とd[1]の中身です。そして、送信先の detail() 関数の引数に id_ と name が指定してあるので、送ったデータをそのまま使うことができ、それを利用して対応したユーザーの年齢と趣味の情報を、 detail.html に渡しているという流れになります。
また、変数にはデフォルト値を設定できます。ひとつの関数に対して、複数のURLを紐づけることもできます。この2つを組み合わせると、以下のようなルーティングも可能です。
@app.route('/list') @app.route('/list/<int:page>') def get_list(page=1): # <int:page> が未指定の場合、page=1が設定される
リダイレクト処理
ログインサイトなんかを作成する場合、ログインが完了してからトップページにリダイレクトするような処理が行われることがあります。これをFlask上で実現するためにはredirectとurlforを使用します。
# トップ画面にリダイレクト @app.route('/redirects') def redirects(): return redirect(url_for('index'))
ここで注意したいのはあくまで、ルート(/とか/members)ではなく、ルートに紐づいている関数名をurl_forの引数に指定する点です。
終わり
以上がFlaskにおけるurl_forを使用した動的なURLの作成とリダイレクト処理の実装方法です。動的なURLはログイン認証サイトなんかを作る際には欠かせないのでぜひ使えるようにしておきましょう。
動くサンプルコードを私のGithubにアップロードしているので、そちらをダウンロードするなりして実際に動かしてもらえるとより理解が深まるかと思います。
次の記事:FlaskによるWebアプリ開発⑥~ファイルダウンロードを実装する
関連記事:【Python】Flask+SQLAlchemyを使って、「ひとこと掲示板」を作る
関連記事:【Python】Flask+MatplotlibでWebアプリにプロットしたグラフを表示する
コメント