app.routeという前提
そもそもこのblueprint
という機能に関して調べるきっかけとなったのはflaskに記述のあるapp.route
という部分がblueprint.route
と記載されていたことです。
そして自分は初学者であるため、app.routeとはflaskにおいて何をしている箇所であるか、しっかりと説明することはできそうになかったため、ここから勉強です。
ということで以下の記事を参考に、flaskの基礎から学びます。
flaskの基本構造
#①モジュールのインポート
from flask import Flask
#②Webアプリ作成
app = Flask(__name__)
#③エンドポイント設定(ルーティング)
@app.route('/')
def hello():
return 'Hello World!'
#④Webアプリ起動~~~~
if __name__ == '__main__':
app.run(debug=True)
まぁ普段はなんとなくここから切りはりして実装していたわけですが、分割すると以下のような機能に分かれるそうです。
1.モジュールのインポート
from flask import Flask
ここは良いですね。flaskモジュールのFlaskクラスをインポートしています。
2.Webアプリ作成
app = Flask(__name__)
appに対してflaskクラスのインスタンスを代入しており、今後app.route()
等でルーティングを定義するなど、さまざまな操作を行います。
3.エンドポイント設定
さてここが問題のポイントです。
@app.route('/')
def hello():
return 'Hello World!'
ここでルーティングが定義されているそうです。
さて、引用元の記事で詳しく説明してもらえているので、シンプルにしますね。
ズバリ、/ にアクセスしたときの処理をappに対して登録している
ということみたいです。
とってもざっくりですが、このようなシンプルな記述でそれが実現できているようです。
しかし私はよくわかっていない箇所を見つけました。
@app.touteの@
ってなんですか。
てっきりflask特有の記述かなとか感じておりましたが、どうやらデコレータ
というPythonの文法だそうです。
デコレータ
まぁ私がわかりやすい説明をするよりも、別の方がとってもわかりやすく解説してくださっているのはいつものことなのですが、勉強のために一度文章化します。
とってもシンプルな表現をするとすれば、デコレータによって関数の「前処理」と「後処理」を行うことができる
というようなものらしいです。
def decorator(func):
def wrapper():
print("デコレータをつけられた関数の前処理をしました。")
func()
print("デコレータをつけられた関数の後処理をしました。")
return wrapper
@decorator
def function():
print("私がデコレータをつけられた関数です。")
function()
このように記載をすると、以下のような実行結果が得られます。
デコレータをつけられた関数の前処理をしました。
私がデコレータをつけられた関数です。
デコレータをつけられた関数の後処理をしました。
function()
を実行すると、デコレータがついているため、decorator()
が先に処理され、内部で前処理と後処理が行われています。
私が理解しやすいなと感じた表現は関数自身が、他の関数に自分自身を引数として渡している
というものである。
何を言っているかわからないと思うが、以下の通り@をつけずに記載するとわかる人も多くなるのではないだろうか。
def decorator(func):
def wrapper():
print("デコレータをつけられた関数の前処理をしました。")
func()
print("デコレータをつけられた関数の後処理をしました。")
return wrapper
def function():
print("私がデコレータをつけられた関数です。")
function = decorator(function)
function()
得られる結果は@のついた実装と同様になります。
重要なのはfunction = decorator(function)
の部分です。
関数に関数自身を受け渡し、結果が返ってくるということだと理解しました。
flaskでのデコレータ
@app.route('/')
def hello():
return 'Hello World!'
さて、flaskの話に戻りますが上記デコレータに関して理解した後ならば、割とすんなりと理解できるのではないでしょうか。
app.route()に対して、「/」というURLにアクセスした際にhello()の内容を紐づけさせている。
つまり「/」というURLにアクセスした際にhello()をflaskによって適切に扱えるよう処理させているということです。
なんだか謎が解けてスッキリした気もしますね。
やっとこさBlueprint
はい、やっとblueprintです。
そもそも私はflask-adminlteをいじっている時にこちらに遭遇しました。
ということでどのような記述がされていたかというと、
from apps.home import blueprint
from flask import render_template, request
from flask_login import login_required
from jinja2 import TemplateNotFound
@blueprint.route('/index')
@login_required
def index():
return render_template('home/index.html', segment='index')
なんとなーくデコレータ部分がappからblueprintに置き換わっているのかなということはわかるかと思います。
手っ取り早く公式ドキュメントに目を通します。
まぁ言っていることをすごくざっくりまとめると、大規模な構成になったときに「分割して実装することができる便利なもの」的な印象を受けました。
と、わかったような口ぶりで言いましたが、すごくふわふわして気持ち悪いですので以下の記事を参考に理解を深めます。
なるほど、そもそも開発をする上でコードを分割するという「わかりやすさ」がこの機能の特徴のようです。
という理解ができるまで苦労していたのですが、以下の記事でもう少し理解が進みました。
この記事から分かる通り、blueprintを二つ作ったとしたらページのURLは
- hogehoge/blueprint1
- hogehoge/blueprint2
となりますし、その下に続ければ
- hogehoge/blueprint1/page1
- hogehoge/blueprint1/page2
- hogehoge/blueprint2/page1
- hogehoge/blueprint2/page2
と全て異なるページが作成されるわけですね。
ということで今回の例を見てみましょう。
from apps.home import blueprint
from flask import render_template, request
from flask_login import login_required
from jinja2 import TemplateNotFound
@blueprint.route('/index')
@login_required
def index():
return render_template('home/index.html', segment='index')
どうやらBlueprintはappsディレクトリ直下のhome/init.pyに記載されていそうです。
from flask import Blueprint
blueprint = Blueprint(
'home_blueprint',
__name__,
url_prefix=''
)
はい、先ほどの内容を理解した後ならば理解ができそうな内容ですね。
この例で言えば、indexに辿り着くにはhome_blueprintの下に入り込む必要があるということすなわち
hogehoge/home_blueprint/index.html
に辿り着けそうです。
はい、辿り着けませんでした。
ここで公式ドキュメントに戻ります。
@simple_page.routeデコレータを利用して関数を結び付け(bind)するとき、blueprintは、後で(blueprintがFlaskインスタンスに)登録されるときにはshow関数をアプリケーション(Flaskインスタンス)へ登録するという意図を記録しておきます。さらに、blueprintは関数のエンドポイントの先頭へ、Blueprintクラスのコンストラクタで与えられたblueprintの名前(この例ではsimple_page)を付加します。blueprintの名前は、URLは変更せず、エンドポイントだけを変更します(訳注: ここでは、simple_page.showのように、エンドポイントの最初にはblueprint名を付け、対応するURLパスの「'/'」や「'/'」の最初にはblueprint名を付けないでURLルールでの対応付けが作成される、という意味合い)。
重要なのはblueprintの名前はURLは変更せず、エンドポイントだけ変更する。
ということです。
要するに先ほど分かった気になっていた
この記事から分かる通り、blueprintを二つ作ったとしたらページのURLは
- hogehoge/blueprint1
- hogehoge/blueprint2
となりますし、その下に続ければ
- hogehoge/blueprint1/page1
- hogehoge/blueprint1/page2
- hogehoge/blueprint2/page1
- hogehoge/blueprint2/page2
と全て異なるページが作成されるわけですね。
という箇所の解釈は間違っていたわけです。
要するにblueprintを抜いたエンドポイントのみをURLに入れれば良いわけですね。
はい、成功したわけですね。
これは確かに開発段階では機能ごと、ページごとにどのようなルーティングをするのか、ファイルを分けることができ、実際にページが動作するときはユーザー側からは全く平常動作に見えるわけですね。
どのような目的を持った機能なのかが理解できればなんら難しいことはなく、理にかなった仕組みでした。