267.ラズパイ無双[13_bottle vs flask]
初回:2022/7/27
Raspberry Pi (ラズベリーパイ、通称"ラズパイ")で何か作ってみようという新シリーズです。カメラ画像をストリーミング出来るサーバーを、bottleベースで作りましたが、flask版も用意しておこうと思います。
P子「bottle信者だと思ってたわ」※1
前提として、ラズパイで簡易的なWebAPIサーバーを使うのであれば、bottleで十分、いや、bottle 一択だという思いは持っていますが、Django ほど大規模な仕組みは必要ないけど、それなりにきちんとしたシステムを作りたいと思っている場合は、flask を使いたいという要望も多いと思います。
ただし、flask については全くの素人ですし、bottleについては、触り始めた程度なので、記述方法や対応方法が間違っているかもしれませんので、そこは各自で調査してください。
P子「読者丸投げね」
まあ、書籍化でも決まれば、きちんと調査しますけど...
P子「まず、ありえない話ね」
【目次】
1.ソース
まずは、今回の bottle と flask のソースを、例の作業部屋に置いておきます。
≪OSDN の作業部屋≫
https://ja.osdn.net/users/chatrun/pf/eu63/files/
OSDN の作業部屋という所に、@IT を作成しました。この中の No267.zip にソースを置いています。
wwwb というのが、bottle 用の環境で、このフォルダに、別にダウンロードしてきた bottle と、waitress をインストールします。
wwwf は、flask 用の環境で、flaskは通常の pipでのインストール、waitress は、インストールでも、bottle と同じく、フォルダ内に作成しても構いません。
フォルダ構成の違いというと、wwwb のテンプレートフォルダは、views で、wwwf のテンプレートフォルダは、templates です。ソースにコメントを書いていますが、それぞれ、テンプレートフォルダ名を変更する機能を持っていますので、無理やり同じフォルダに構築することも可能でしょう。
ただ、前回も書いたように、bottle派もflask派も互換運用は求めていないと思いますので、基本は変えずに差分の説明をしたいと思います。
2.サーバー部分の差分
ogServer.py の差分の説明です。
No | 行番号 | bottle | flask |
---|---|---|---|
① | 18,19 | import bottle | import flask |
② | 25,26 | def enc( key ) 定義 | なし |
③ | 63,67 | app = bottle.Bottle() | app = flask.Flask(__name__) |
④ | 84,85 | if subapp : app.merge(subapp) | if subapp : app.register_blueprint(subapp) |
⑤ | 108,109 | url = bottle.request.params.get( "action",name ) | url = flask.request.args.get( "action",name ) |
⑥ | 121,122 | prmDicにparamsセット | なし |
⑦ | 124,125 | params = dict( { "title":TITLE ,"enc":enc } , **modules, **prmDic ) | params = dict( { "title":TITLE } , **flask.request.args , **prmDic ) |
⑧ | 127,128 | return bottle.template(url,params) | return flask.render_template(url,params=params,**modules) |
⑨ | 132,139 | static ルーティング処理 | なし |
⑩ | 155,159 | bottle.response.content_type = 'multipart/x-mixed-replace;boundary=frame' return gen(cam,argp.skip) |
return flask.Response(gen(cam,argp.skip), mimetype='multipart/x-mixed-replace; boundary=frame') |
⑪ | 169,172 | bottle.response.content_type = 'image/jpeg' return cam.get_frame() |
return flask.Response(cam.get_frame() , mimetype='image/jpeg') |
① flask の import は、
from flask import Flask , request , render_template , Response
というのが、普通でしょう。今回は、bottle との差分が判りやすい様に、flask を個々に付けています。
② def enc の箇所は、bottle で日本語パラメータを使用する場合のUTF-8 変換ロジックです。flaskでは、どうするのか分からなかったので、今回は未対応です。
③ app オブジェクトの生成箇所です。テンプレートフォルダを変更する場合の方法は異なります。
④ subapp としてルーティングをモジュールで実行する場合の組み込み方法については、bottle は、merge を使用しており、flaskは、register_blueprint を使用しています。
≪参考資料≫
https://doitu.info/blog/5ac362762e280f0095116f10
Bottleでファイルを分割する方法
≪参考資料≫
https://rurukblog.com/post/Flask-blueprint/
【Flask】blueprintの使い方
⑤ requestパラメータからの値取得は、bottle はparams.getメソッド、flask は、args.getメソッドを使用します。
⑥ テンプレートに辞書を渡す方法は、少し異なります。実はこのあたりはよく分かっていませんので、もしかすると、もっと効率的な方法があるのかもしれません。
⑦ パラメータの生成方法です。
⑧ テンプレートの適用方法です。
⑨ static ルーティング処理については、flask ではルーティングの記述が不要です。
⑩ content_typeの返し方と、ストリームのgen関数の返し方が異なります。
⑪ snapshot ルーティング処理も、⑩と同様です。
3.フィルター部分の差分
フィルター部分の差分の解説の前に、差分のないプログラム部分を説明しておきます。
camera.py
test_module.py
については、bottle と flask で共通です。test_module.py については、インスタンス化して、ogServer.py に渡して、辞書にセットして、テンプレートに渡すだけなので、共通化できます。フィルターについても、実際はインスタンスをogServer.py に渡して、camera.py に渡すだけなので、共通化できます。
共通化できていないのは、サブアプリで、これは bottle や flask のルーティングとモジュール組み込みに関連しているため、差分が出来ます。そして、実際にはフィルターやモジュールは、Web画面からの値の受け取りや受け渡しを行うため、それぞれに特化した方法になりますが、def new_obj() という関数内だけにとどめています。
test_filter.py が、差分のあるファイルで、new_obj 関数内だけが異なります。
今回、あえて関数内で import していますが、単に差分を判りやすくしただけです。実際に bottle や flask を使用る場合は、先頭にmport するでしょう。ここでも、import flask としていますが、from flask import Blueprint , request と記述していて構いません。
微妙な注意点としては、ルーティング部分で、bottle は method='POST' と文字列で、flask は methods=['POST'] とリストで記述します。
最後に、JSON形式のリクエストの受け取り方が少し異なります。この辺は、flask の方がシンプルな気がします。
4.テンプレート部分の差分
まず、デフォルトのテンプレートフォルダ名が異なります。bottle は、views で、flask は、templates となっています。すでに、ogServer.py のソースにコメントしているように、bottle でテンプレートフォルダを変える場合は、
bottle.TEMPLATE_PATH += ['./templates'] # views → templates
と記述し、flask でテンプレートフォルダ名を変える場合は、
app = flask.Flask(__name__, template_folder="views") # templates → views
と記述します。
① 変数名の参照
header.html の {{title}} の記述は、bottle も flask も共通で、オブジェクトを画面に書き込む(=変数の参照)場合、{{ 変数名 }} の記号を使用します。
② 制御文
index.html で、header.html を include していますが、これは制御文を使用しています。
bottle では、<% 制御文 %> と記述し、flask では、{% 制御文 %} と記述します。
bottle は、<% include( "header.html" ) %>
flask は、{% include( "header.html" ) %}
となります。
③ 変数の代入(制御文)
test.html で変数の代入を行っていますが、これも、bottle と flask で少し異なります。
bottle は、<% obj = modname %>
flask は、{% set obj = modname %}
のように、flask では、set という記号を付ける必要があります。
④、⑤ if や for などのブロック処理(制御文)
test.html で辞書をループさせて中身を取り出していますが、通常ブロック処理は、python特有のインデントを使ったブロック定義が使えません。そのため、終了条件を記述する必要があります。bottle では、for や if でも、<% end %> を使用し、flask では、{% endfor %} や {% endif %} と記述します。
bottle
<% for k,v in obj.empData.items() : %>
{{k}}{{v}}
<% end %>
flask
{% for k,v in obj.empData.items() : %}
{{k}}{{v}}
{% endfor %}
bottle では、<% と %> で囲えば、制御ブロックとして記述でき、先頭が % で始めれば制御文として評価されます。個人的には、% 一文字での記述は簡易的ですが、例え1行でも、<% と %> で囲って、制御文であることが一目でわかる方が好みです。
5.まとめ
bottle と flask は、ほとんど同じように使えると思います。後は好みの問題だと思います。もちろん、機能的には、flask に軍配が上がるかもしれませんが、逆に複雑にしたくない場合は、bottle が良いと思います。
bottle に、SQLiteというデータベースを使用すれば、結構本格的なWebアプリも作れます。ラズパイ上に簡易システムを構築する場合、bottle で十分でしょう。
今後は、bottle 中心にソースを書いていきますが、flask派の方も、ほんの少しの修正で適用できますので、ぜひ、お楽しみにしていてください。
ほな、さいなら
======= <<注釈>>=======
※1 P子「bottle信者だと思ってたわ」
P子とは、私があこがれているツンデレPythonの仮想女性の心の声です。