Pythonでウェブアプリを作りたい:Flask編

動機
日曜大工的にやりたいことを思いついて,ウェブでアプリとして動かしたい.
やりたいことを実現するためのライブラリがPythonインターフェイスがある.
最近はPythonに慣れてきたこともある.
方針
Pythonのウェブアプリ開発フレームワークについて理解してウェブアプリを開発する.
ただし,あくまで日曜大工的にやる.

Djangoはひとまずやめて,Flaskを使ってみる.
調べてみたら,Flaskの簡単な使い方というQiitaのページが見つかった.
3年前の記事だけど,基本的な使い方は生きているっぽい.
pythonコードもかなり読んだそのまま.
と思ったがFlaskのページにあるコードは書き方が変わっている.
最新の書き方になっているものを参照したほうがよさそうである.
とりあえず眺めるのはとほほのFlask入門がよさそうである.
適当に公式のドキュメントを見ることにする.

よくよく見ると
$ flask run
でもいいし
$ python app.py
でもよいらしい.

色々と調べてみるとやりたいことの基礎は色々な記事になっていることがわかった.
まず,Flaskで作ったウェブアプリをApacheで公開するために必要なことはmod_wsgi (Apache)に書いてある.
apache2.4 が動いているUbuntuが既にあるので,
$ sudo apt-get install libapache2-mod-wsgi-py3
で入る.
site-enabled/
にあるバーチャルホストの設定ファイルに
WSGIDaemonProcess app user=www-data group=www-data threads=5
WSGIScriptAlias /app /data/www/app/application.wsgi
<Directory /data/www/app/>
WSGIProcessGroup application1
WSGIApplicationGroup %{GLOBAL}
Require all granted
</Directory>
を追加する.userとgroupは以下のwsgiファイルの所有者にしないと動かないみたいだ.
application.wsgi
を作成して/data/www/app/
下に配置する.
import sys
sys.path.insert(0, '/data/py/')
from visg import app as application
それから実際に動かしたいpythonファイルは/data/py/visg.py
に
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
name = "Hello World"
return name
@app.route('/good')
def good():
name = "Good"
return name
if __name__ == "__main__":
app.run()
としておく.
これでsudo service apache2 restart
してウェブサイトの/app
にアクセスしてみるとHello world
が表示され,/app/good
にアクセすると,Good
が表示される.
application1.wsgi
は単なるpythonファイルっぽくて,普通にpythonコードを書いても動く.
わざわざapplication1.wsgi
とvisg.py
を分ける理由はよくわからないがセキュリティ面の問題だろうか?
参考にしたウェブページ:
- https://msiz07-flask-docs-ja.readthedocs.io/ja/latest/deploying/mod_wsgi.html
- https://qiita.com/Riskchan/items/e799adedcf683f352873
- https://blog.mktia.com/how-to-run-python-scripts-in-apache-using-mod-wsgi/
- https://a4dosanddos.hatenablog.com/entry/2015/08/31/141852
これでとりあえずウェブ上にアプリを公開できるようになったので,Flaskの使い方を理解する.

どうもアプリを更新したらapacheの再起動が必要らしい.まぁそりゃそうか.

まずはフロントエンドの使い方を学ぶ.
基本的にFlaskの簡単な使い方を参考にして,適当に補足情報を足していく.
テンプレートの使い方
ディレクトリ構造を以下のような感じにして動かしたい.
app
├── templates
│ ├── index.html
│ └── layout.html
└── visg.py
layout.html
とindex.html
は
<!doctype html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
{% block content %}
<!-- ここにメインコンテンツを書く -->
{% endblock %}
</body>
</html>
{% extends "layout.html" %}
{% block content %}
<h1>vis-g</h1>
{% endblock %}
テンプレートの書き方は昔からだいたい変わらないっぽい.助かる.
また,visg.py
を以下のように書く.
from flask import Flask, render_template
app = Flask(__name__, template_folder='templates')
@app.route('/')
def index():
title = "vis-g | visualize point and space groups"
name = "Hello World"
return render_template('index.html', title=title, name=name)
@app.route('/good')
def good():
name = "Good"
return name
if __name__ == "__main__":
app.run(debug=True)
これでpython visg.py
を実行してブラウザでアクセスしてみればテンプレートが反映されていた.
Qiitaの記事との違いは,テンプレートのhtmlファイルが異なるディレクトリにあって,template_folder='templates'
というのをapp
宣言時に追加している点.

CSS/Javascriptファイルを配置する
テンプレートを配置できるようになったのでCSSやJavascriptファイルも使いたい.
これについてはflaskのパスを指定するを参考にした.
ディレクトリ構造は以下のようにしたい.
app
├── static
│ ├── css
│ │ └── stylesheet.css
│ └── js
├── templates
│ ├── index.html
│ └── layout.html
└── visg.py
static
ディレクトリを追加した感じになる.
visg.py
で,今度はstatic_folder
を指定する.修正して,以下のようにapp
を宣言する.
(前略)
app = Flask(__name__, static_folder='static', template_folder='templates')
(後略)
それからtemplates/layout.html
のheadタグの下にstatic/css/styplesheet.css
の存在を知らせる.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/stylesheet.css') }}">
<title>{{ title }}</title>
</head>
<body>
{% block content %}
{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>
重要なところは<link rel="stylesheet" href="{{ url_for('static', filename='css/stylesheet.css') }}">
というところ.
ちなみに,bootstrapとか色々追加している.
同じようにjsファイルを追加できることが容易に想像できる.

flaskのパスを指定するにはpython実行ファイルの分割も書いてあるようだ.そのうち使うかもしれないのでメモ.

調べてみたら,FlaskにはBlueprintというモジュールが標準であって,それでアプリを分割する方が便利そう.
【Flask】blueprintの使い方

公式のチュートリアルをみると,色々と手の込んだ書き方がされているのでそれを参考にしよう.
公式ドキュメント的な書き方
ディレクトリ構造は以下のようにする.
point
├── __init__.py
├── static
│ ├── css
│ │ └── stylesheet.css
│ └── js
└── templates
├── index.html
└── layout.html
from flask import Flask, render_template
static_dir = './static'
template_dir = './templates'
def create_app():
# create and configure the app
app = Flask(__name__, instance_relative_config=True, static_folder=static_dir, template_folder=template_dir)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
@app.route('/')
def index():
title = "vis-g"
name = "hoge"
return render_template('index.html', title=title, name=name)
@app.route('/good')
def good():
name = "Good"
return name
return app
参考:公式ドキュメント - Application Setup
公式的には,app
の宣言も関数にするらしい.
あとインスタンスも作ってくれる.
テストするためには,vis
ディレクトリで(vis/point
ディレクトリではなく)
$ export FLASK_APP=point
$ flask run
この場合,Apacheサーバで実行するために以下のように書き変える.(mod_wsigの設定は別に必要:https://zenn.dev/link/comments/2b29d1d331a3cd 参考)
import sys
sys.path.insert(0, '/data/www/app')
from point import create_app
application = create_app()