flaskとbottleを比較する


flaskのことをいろいろ調べていたら、同じようなPythonのWebフレームワークとしてbottleというのがあることが分かりました。
WEBで見る限り、非常によく似ていたので違いを試してみました。

flask も bottle も、見た目は全く同じにすることができます。




<インストール>
どちらもpipを使ってインストールを行います。
sudo apt-get install python-pip
sudo apt-get install python-setuptools
sudo pip install flask


sudo apt-get install python-pip
sudo apt-get install python-setuptools
wget https://bottlepy.org/bottle.py
sudo pip install bottle

<Pythonコード>
それぞれで使用するpythonコードは以下のようになります。
上がflask、下がbottleです。
from flask import Flask, render_template
import datetime
app = Flask(__name__)

@app.route("/")
def index():
   print "Start index"
   now = datetime.datetime.now()
   timeString = now.strftime("%Y-%m-%d %H:%M")
   templateData = {
      'title' : 'HELLO!',
      'name' : 'flask!',
      'ampm' : 1,
      'time': timeString
      }
   #./templates/sample.html
   return render_template('sample.html', **templateData)

if __name__ == "__main__":
   app.run(host='0.0.0.0', port=8080, debug=True)

from bottle import route, run, view, static_file, url
import datetime

@route('/static/<filepath:path>', name='static_file')
def static(filepath):
    return static_file(filepath, root="./static")

@route('/')
@view("sample")
def index():
   print "Start index"
   now = datetime.datetime.now()
   timeString = now.strftime("%Y-%m-%d %H:%M")
   #../views/main.html
#   return template('sample', title='HELLO', name='bottle!', ampm=1, time=timeString, url=url)
   return dict(title='HELLO', name='bottle!', ampm=1, time=timeString, url=url)

if __name__ == "__main__":
    run(host='0.0.0.0', port=8080, debug=True, reloader=True)

<テンプレートファイル>
それぞれで使用するテンプレートファイルは以下のようになります。
上がflask、下がbottleです。
<!DOCTYPE html>
   <head>
      <title>{{ title }}</title>
      <link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"/>
   </head>

   <body>
      <h1>Hello, {{ name }}</h1>
{% if ampm == 0 %}
      <h2>Good Morning</h2>
      <h2>おはようございます</h2>
{% else %}
      <h2>Good Evening</h2>
      <h2>こんばんは</h2>
{% endif %}
      <h2>The date and time on the server is: {{ time }}</h2>
      <ul>
{% for no in range(10) %}
        <li>Number {{no}}</li>
{% endfor %}
      </ul>
      <img src="{{url_for('static', filename='images/smile-icon.png')}}">
   </body>
</html>

<!DOCTYPE html>
   <head>
      <title>{{ title }}</title>
      <link type="text/css" rel="stylesheet" href="{{ url('static_file', filepath='style.css') }}"/>
   </head>

   <body>
      <h1>Hello, {{ name }}</h1>
% if ampm == 0:
      <h2>Good Morning</h2>
      <h2>おはようございます</h2>
% else:
      <h2>Good Evening</h2>
      <h2>こんばんは</h2>
% end
      <h2>The date and time on the server is: {{ time }}</h2>
      <ul>
% for no in range(10):
        <li>Number {{no}}</li>
% end
      </ul>
      <img src="{{url('static_file', filepath="images/smile-icon.png")}}">
   </body>
</html>

<CSSファイル>
どちらも同じCSSファイルを使いました。
html { font-family: sans-serif; background: #eee; padding: 1rem; }
body { max-width: 960px; margin: 0 auto; background: white; }
h1 { font-family: serif; color: #377ba8; margin: 1rem 0; }
a { color: #377ba8; }
hr { border: none; border-top: 1px solid lightgray; }
nav { background: lightgray; display: flex; align-items: center; padding: 0 0.5rem; }
nav h1 { flex: auto; margin: 0; }
nav h1 a { text-decoration: none; padding: 0.25rem 0.5rem; }
nav ul  { display: flex; list-style: none; margin: 0; padding: 0; }
nav ul li a, nav ul li span, header .action { display: block; padding: 0.5rem; }
.content { padding: 0 1rem 1rem; }
.content > header { border-bottom: 1px solid lightgray; display: flex; align-items: flex-end; }
.content > header h1 { flex: auto; margin: 1rem 0 0.25rem 0; }
.flash { margin: 1em 0; padding: 1em; background: #cae6f6; border: 1px solid #377ba8; }
.post > header { display: flex; align-items: flex-end; font-size: 0.85em; }
.post > header > div:first-of-type { flex: auto; }
.post > header h1 { font-size: 1.5em; margin-bottom: 0; }
.post .about { color: slategray; font-style: italic; }
.post .body { white-space: pre-line; }
.content:last-child { margin-bottom: 0; }
.content form { margin: 1em 0; display: flex; flex-direction: column; }
.content label { font-weight: bold; margin-bottom: 0.5em; }
.content input, .content textarea { margin-bottom: 1em; }
.content textarea { min-height: 12em; resize: vertical; }
input.danger { color: #cc2f2e; }
input[type=submit] { align-self: start; min-width: 10em; }

<ディレクトリ構成>
flask
Python Code $HOME/flask/sample.py
テンプレート $HOME/flask/templates/sample.html
css $HOME/flask/static/style.css
images $HOME/flask/static/images/smile-icon.png

bottle
Python Code $HOME/bottle/sample.py
テンプレート $HOME/bottle/views/sample.html
css $HOME/bottle/static/style.css
images $HOME/bottle/static/images/smile-icon.png

<起動画面>




<日本語マニュアル>
flask
https://a2c.bitbucket.io/flask/tutorial/index.html

bottle
https://bottl-translate-ja.readthedocs.io/en/latest/01_1_tutorial.html


<英語チュートリアル>
flask
https://flask.palletsprojects.com/en/1.1.x/tutorial/

bottle
http://bottlepy.org/docs/dev/tutorial.html



少し前まではflaskはpython3に対応してなかったみたいですが、今は対応しています。
ほとんど違いが無いのですが、テンプレートファイルに渡すテンプレートデータの渡し方が、flaskのほうがスマートだと思います。
なお、flaskでもbottleと同じように、ずらずらとテンプレートデータを並べて指定することができます。

flask
templateData = {
      'title' : 'HELLO!',
      'name' : 'flask!',
      'ampm' : 1,
      'time': timeString
}
return render_template('sample.html', **templateData)

bottol
return dict(title='HELLO', name='bottle!', ampm=1, time=timeString, url=url)

また、テンプレートファイルの指定方法が違います。
flaskではrender_template()の第一引数でテンプレートファイルを指定しますが、
bottleでは@view("sample")で指定します。

bottleの決定的な違いは、テンプレート内にpythonコードを埋め込むことができます。
<!DOCTYPE html>
   <head>
      <title>{{ title }}</title>
      <link type="text/css" rel="stylesheet" href="{{ url('static_file', filepath='style.css') }}"/>
   </head>

   <body>
      <h1>Hello, {{ name }}</h1>
% if ampm == 0:
      <h2>Good Morning</h2>
      <h2>おはようございます</h2>
% else:
      <h2>Good Evening</h2>
      <h2>こんばんは</h2>
% end
      <h2>The date and time on the server is: {{ time }}</h2>
<%
import datetime
import locale
d = datetime.datetime.today()
date ='{0:d}年{1:d}月{2:d}日'.format(d.year, d.month, d.day)
%>
      <h2>date is: {{ date }}</h2>
      <ul>
% for no in range(10):
        <li>Number {{no}}</li>
% end
      </ul>
      <img src="{{url('static_file', filepath="images/smile-icon.png")}}">
   </body>
</html>




この機能はflaskにはありません。
この違いは使っているテンプレートエンジンの違いによるものです。

flaskのテンプレートエンジン
Jinja2 Template Engine

bottleのテンプレートエンジン
Simple Template Engine

ドキュメントはJinja2(ニンジャと呼びます)の方がしっかりしています。
bottleの方が一見、高機能に見えますが、ロジックと見た目(テンプレート)を分離するという点では
このような機能を持たないflaskの方が正しい実装に見えます。