Open10

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

Junji FujimotoJunji Fujimoto

動機

日曜大工的にやりたいことを思いついて,ウェブでアプリとして動かしたい.
やりたいことを実現するためのライブラリがPythonインターフェイスがある.
最近はPythonに慣れてきたこともある.

方針

Pythonのウェブアプリ開発フレームワークについて理解してウェブアプリを開発する.
ただし,あくまで日曜大工的にやる.

Junji FujimotoJunji Fujimoto

Djangoはひとまずやめて,Flaskを使ってみる.

調べてみたら,Flaskの簡単な使い方というQiitaのページが見つかった.
3年前の記事だけど,基本的な使い方は生きているっぽい.
pythonコードもかなり読んだそのまま.

と思ったがFlaskのページにあるコードは書き方が変わっている.
最新の書き方になっているものを参照したほうがよさそうである.

とりあえず眺めるのはとほほのFlask入門がよさそうである.
適当に公式のドキュメントを見ることにする.

Junji FujimotoJunji Fujimoto

色々と調べてみるとやりたいことの基礎は色々な記事になっていることがわかった.

まず,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/下に配置する.

/data/www/app/application.wsgi
import sys
sys.path.insert(0, '/data/py/')
from visg import app as application

それから実際に動かしたいpythonファイルは/data/py/visg.py

/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.wsgivisg.pyを分ける理由はよくわからないがセキュリティ面の問題だろうか?

参考にしたウェブページ:

これでとりあえずウェブ上にアプリを公開できるようになったので,Flaskの使い方を理解する.

Junji FujimotoJunji Fujimoto

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

Junji FujimotoJunji Fujimoto

まずはフロントエンドの使い方を学ぶ.

基本的にFlaskの簡単な使い方を参考にして,適当に補足情報を足していく.

テンプレートの使い方

ディレクトリ構造を以下のような感じにして動かしたい.

app
├── templates
│   ├── index.html
│   └── layout.html
└── visg.py

layout.htmlindex.html

layout.html
<!doctype html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
{% block content %}
<!-- ここにメインコンテンツを書く -->
{% endblock %}
</body>
</html>
index.html
{% extends "layout.html" %}
{% block content %}
<h1>vis-g</h1>
{% endblock %}

テンプレートの書き方は昔からだいたい変わらないっぽい.助かる.

また,visg.pyを以下のように書く.

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宣言時に追加している点.

Junji FujimotoJunji Fujimoto

CSS/Javascriptファイルを配置する

テンプレートを配置できるようになったのでCSSやJavascriptファイルも使いたい.
これについてはflaskのパスを指定するを参考にした.
ディレクトリ構造は以下のようにしたい.

app
├── static
│   ├── css
│   │   └── stylesheet.css
│   └── js
├── templates
│   ├── index.html
│   └── layout.html
└── visg.py

staticディレクトリを追加した感じになる.
visg.pyで,今度はstatic_folderを指定する.修正して,以下のようにappを宣言する.

visg.py
(前略)

app = Flask(__name__, static_folder='static', template_folder='templates')

(後略)

それからtemplates/layout.htmlのheadタグの下にstatic/css/styplesheet.cssの存在を知らせる.

templates/layout.html
<!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ファイルを追加できることが容易に想像できる.

Junji FujimotoJunji Fujimoto

公式のチュートリアルをみると,色々と手の込んだ書き方がされているのでそれを参考にしよう.

公式ドキュメント的な書き方

ディレクトリ構造は以下のようにする.

point
├── __init__.py
├── static
│   ├── css
│   │   └── stylesheet.css
│   └── js
└── templates
    ├── index.html
    └── layout.html
__init__.py
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 参考)

application.wsgi
import sys

sys.path.insert(0, '/data/www/app')
from point import create_app
application = create_app()

参考: 公式ドキュメント - mod_wsgi