FlaskでのWebアプリ作成方法(Windows)を解説します。作成するアプリは、同位体年代測定など、理系・研究者向けのちょっとした専門計算サイトです。
今回の題材は、その中の『同位体年代測定アプリ』です。要するに、回帰直線を引くアプリケーションです。笑
Point!
- 環境構築
- Anconda Promptの使用方法
- Flaskのファイル構成
- app.pyの基本形
- テンプレートの作成と継承
- 計算ロジックの組み込み
環境を構築する(Windows版)
Anacondaをインストール
Anacondaをインストールしてください。解説は、他のサイトを引用します。
👇Windows版Anacondaのインストール(Python Japan)
https://www.python.jp/install/anaconda/windows/install.html👇Anacondaのインストール (Let’s プログラミング)
https://www.javadrive.jp/python/install/index5.html👇Anaconda(Python3)インストール手順<Windows用>(IT入門書籍)
https://sukkiri.jp/technologies/ides/anaconda-win_install.htmlAnaconda Promptで仮想環境の作成とpip install Flask
1.Anaconda Promptを起動
Anaconda Promptを起動します。
2.Flask用のファイルを作成
どこでも良いので、Flask用のファイルを作成します。私は、今回『calcsite』という名前のフォルダ作りました。後ほどファイル構成については、解説します。
3.仮想環境の構築
3-1. 仮想環境の作成
まず、現在の状態を確認します。『conda env list』と入力します。
最初は、『base』という環境しかないはずです。
(base) C:\Users>conda env list
# conda environments:
#
base * C:\Users\Anaconda3
次に仮想環境を構築します。以下のコマンドプロントを入力します。環境名はお好みで変更してください。私は、”flaskenv”とします。
(base) C:\Users>conda create -n 環境名 python
また、pythonのバージョンを指定する場合、以下のようにバージョンを指定します。
(base) C:\Users>conda create -n 環境名 python==3.6
これで、現在の状態を確認すると以下のよう『env/flaskenv』が作成できました。
以下、環境名=flaskenvとします。
(base) C:\Users>conda env list
# conda environments:
#
base * C:\Users\Anaconda3
flaskenv C:\Users\Anaconda3\envs\flaskenv
3-2. 仮想環境の有効化
仮想環境を有効化します。以下のように『conda activate flaskenv』を入力します。
(base) C:\Users>conda activate flaskenv
先頭の(base)が(flaskenv)となれば、flaskenvの仮想環境が有効化されています。
(flaskenv) C:\Users>
3-3. 必要なモジュールインストール
今回は、以下のモジュールをインストールします。
- Flask
- numpy
- matplotlib
- gunicorn
- Flask-SQLAlchemy (DBを使う場合のみ、今回は使用しません)
仮想環境で必要なモジュールをインストールには『pip install モジュール名』をAnacoda Promptに入力します。
以下、flaskを例にインストールします。
(flaskenv) C:\Users>pip install flask
他に必要なモジュールについても同様にインストールします。
以下の『pip list』でインストールしたモジュールを確認できます。以下は長くなるので、一部抜粋です。
(flaskenv) C:\Users>pip list
Package Version
---------------- ---------
Flask 2.0.3
Flask-SQLAlchemy 2.5.1
gunicorn 20.1.0
Jinja2 3.0.3
matplotlib 3.5.1
numpy 1.22.3
pip 21.2.4
SQLAlchemy 1.4.32
ここまででFlaskを始めるための準備は終了です。
フォルダ構成
完成形のファイル構成は以下のようになります。
calcsite
│ app.py
│ Procfile
│ requirements.txt
│
├─static
│ ├─css
│ │ bootstrap.min.css
│ │ sidebars.css
│ │ style.css
│ │
│ └─js
│ bootstrap.bundle.min.js
│ form.js
│ sidebars.js
│
└─templates
│ base.html
│ error.html
│ home.html
│
└─geochemi
graph.html
RbSr.html
アプリ内のロジック作成(app.pyの作成)と起動
app.pyの作成
アプリのロジックを作成します。app.pyをFlask用に作成したフォルダに作成し、以下のコードを記載します。
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
if __name__ == "__main__":
app.run(debug=True)
app.pyの起動
次にAnaconda Promptで実行します。flask用のファイル(ここでは、calcsite)に移動して、『python app.py』を実行。以下のようにコードが出れば成功です。http://127.0.0.1:5000/へ移動すると、『Hello, World!』が出ます。
(flaskenv) C:\Users\calcsite>python app.py
* Serving Flask app 'app' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Restarting with stat
* Debugger is active!
* Debugger PIN: 519-676-480
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
メインページの作成(templates)
続いて、メインページを作っていきます。ここではhtml、css、javascriptについては詳しくは説明しません。app.pyがhtmlをどのように呼び出すのか、解説します。
render_template()の使い方(home.htmlの作成と表示)
👇こんな感じのサイトを作りました。
このサイトを呼び出すように、以下のようにapp.pyを書き換えます。先ほどのHello worldを記述したapp.pyからの変更点は、以下の2点。
- render_templateをインストール
- def home()に変更し、return先をhome.htmlに変更。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
return render_template('home.html')
if __name__ == "__main__":
app.run(debug=True)
その後、先ほどと同様にAnconda Promptで実行。
(flaskenv) C:\Users\calcsite>python app.py
作成したhtmlが表示されはずです。
テンプレートの継承(base.htmlの作成)
navbarやsidebarなど何度も使い回しする部分について、base.htmlに記載して、どのページでも使い回すことができます。具体的には、base.htmlには私は以下を記載しました。
- htmlの基本構文
- cssやjsなどのstaticファイルからの読み込むためのコード
- navbarとsidebarなど、画面遷移しても常に出ている部分
テンプレートの継承は、base.htmlには以下を記載。
- headに{% block head %}{% endblock %}を記載
- {% block body %}{% endblock %}をページごとに入れ替えたい部分に記載
<html lang="ja" class=" js">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<!-- title -->
<title>エンジニアリング計算</title>
{% block head %}{% endblock %}
<head>
<body>
<nav>
~~~~~~~~~~省略~~~~~~~~~~~~~~~~~~
</nav>
<div id="sidebar">
~~~~~~~~~~省略~~~~~~~~~~~~~~~~~~
<div>
{% block body %}{% endblock %}
<script src="static/js/sidebars.js"></script>
<script src="static/js/form.js"></script>
</body>
</html>
引き継ぎ先のhome.htmlには、以下を記載します。
- 先頭に{% extends ‘base.html’ %}を記載して、base.htmlから引き継ぐ。
- {% block body %}と{% endblock %}の間に中身を記載する。
{% extends 'base.html' %}
{% block body %}
<div class="main">
<div class="l-main">
<h2 id="header2"><a href="/">技術計算ツール</a></h2>
~~~~~~~~~~省略~~~~~~~~~~~~~~~~~~
</div>
</div>
{% endblock %}
app.pyはそのままです。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
return render_template('home.html')
if __name__ == "__main__":
app.run(debug=True)
Anaconda Promptで実行して、先ほどと同じ画面が出れば成功です。
計算ページの作成
データ入力画面の作成(RbSr.htmlの作成)
👇以下のページを作成します。これは、ポイントとしては以下になります。
- <form> のactionとmethods属性の入力
- <input>のname入力
増減ができるテーブルの作り方と数式の表示(MathJax)については、Javascriptの要素が強いので省略。別記事にします。
{% extends 'base.html' %}
{% block body %}
<div class="main">
<div class="container">
<h2 class="l-main-ttl" id="header1"><a href="/">同位体年代測定 Rb-Sr法</a></h2>
<div>
<h3>同位体年代測定の基本式</h3>
<div id="f">
<div>
\[
\frac{87Sr}{86Sr} =
\left(\frac{87Sr}{86Sr}\right)_0 + \frac{87Rb}{86Sr} \left(e^{λt}-1\right)
\]
</div>
</div>
</div>
<br>
<h3>データベース</h3>
<form action="/graph" method = "POST">
<table class="table table-striped table-hover">
<thread>
<tr class="">
<th scope="col">#</th>
<th scope="col">87Sr/86Sr</th>
<th scope="col">87Rb/86Sr</th>
<th scope="col"></th>
</tr>
</thread>
<tbody id="RbSr_table">
<tr id="num1">
<td>0</td>
<td><input type="number" name="Sr1" id="" step="0.000001" class="form-control" max="1" min="0"></td>
<td><input type="number" name="Rb1" id="" step="0.000001" class="form-control" max="1" min="0"></td>
<td>削除不可</td>
</tr>
<tr id="num2">
<td>1</td>
<td><input type="number" name="Sr1" id="" step="0.000001" class="form-control" max="1" min="0"></td>
<td><input type="number" name="Rb1" id="" step="0.000001" class="form-control" max="1" min="0"></td>
<td>削除不可</td>
</tr>
<tr>
<input type="button" value="データ入力欄追加" onclick="addForm2()">
</tr>
</tbody>
</table>
<p style="color: red;">※2点以上のデータを入力してください</p>
<br>
<input type="submit" value="Create Graph & claculation">
</form>
</div>
</div>
{% endblock %}
home.htmlからRbSr.htmlへの移動
app.pyにロジックを追加します。『/RbSr』へ遷移したとき、geochemi/RbSr.htmlを返すように記載を追加(9~11行目)。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
return render_template('home.html')
@app.route('/RbSr', methods=['GET', 'POST'])
def RbSr():
return render_template('geochemi/RbSr.html')
if __name__ == "__main__":
app.run(debug=True)
base.htmlやhome.htmlのRb-Sr法と書かれた部分に<a>タグで遷移先『/RbSr』を指定します。
以下、記載例です。
<span class="ss"><a href="/RbSr">Rb-Sr法</a></span>
計算結果表示ページ(RbSr.html→graph.html)への遷移
ここは要素が多いので、以下のように区切ろうと思います。
- formからの値をリストとして受け取る
- formから受け取った値をグラフ化
- graph.htmlへの結果の受け渡し
- エラーハンドリング
<form>からの値をリストとして受け取る
先ほどRbSr.htmlの<form>には以下のように属性を与えています。また、<input> に”Sr1”と”Rb1”というname属性を与えています
<form action="/graph" method = "POST">
<table class="table table-striped table-hover">
~~~~~~~~~~省略~~~~~~~~~~~~~~~
<tbody id="RbSr_table">
<tr id="num1">
<td>0</td>
<td><input type="number" name="Sr1" id="" step="0.000001" class="form-control" max="1" min="0"></td>
<td><input type="number" name="Rb1" id="" step="0.000001" class="form-control" max="1" min="0"></td>
<td>削除不可</td>
</tr>
~~~~~~~~~~省略~~~~~~~~~~~~~~~
</form>
これを受けるapp.pyのロジックを作成します。先ほどまでのapp.pyに『/graph』を受けるロジックを追加します。さらに、<form>内の<input>をrequest.form.getlist(name属性)でリストとして取得します。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
return render_template('home.html')
@app.route('/RbSr', methods=['GET', 'POST'])
def RbSr():
return render_template('geochemi/RbSr.html')
@app.route('/graph',methods = ['POST', 'GET'])
def graph():
if request.method == 'POST':
Rb_list = request.form.getlist('Rb1')
Sr_list = request.form.getlist('Sr1')
return render_template('geochemi/graph.html')
if __name__ == "__main__":
app.run(debug=True)
formから受け取った値をグラフ化
/graphを受けるロジックをコードに沿って説明します。流れとしては、以下です。
- モジュールをimport
- <form>から受け取ったstrをfloatへ変換と行数のカウント
- グラフを作成する(fig)
- figでは表示できないので、base64に変換
- 計算値と図をgraph.htmlへ引き渡す
全体のコードは以下のようになります。
from flask import Flask, render_template, redirect, request,
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Agg')
import io
import numpy as np
from io import BytesIO
import base64
def fig_to_base64_img(fig):
"""画像を base64 に変換する。
"""
# png 形式で出力する。
io = BytesIO()
fig.savefig(io, format="png")
# base64 形式に変換する。
io.seek(0)
base64_img = base64.b64encode(io.read()).decode()
@app.route('/graph',methods = ['POST', 'GET'])
def graph():
if request.method == 'POST':
try:
Rb_list = request.form.getlist('Rb1')
Sr_list = request.form.getlist('Sr1')
Rb_list = [float(n) for n in Rb_list ]
Sr_list = [float(n) for n in Sr_list ]
n = len(Rb_list)
x = Rb_list
y = Sr_list
fig = plt.figure(figsize=(15,6))
# ax = plt.axes()
ax = fig.add_subplot(111)
cf1 = np.polyfit(x,y,1)
func1 = np.poly1d(cf1)
fig, ay = plt.subplots()
ay.invert_yaxis()
plt.plot(x, func1(x),label='d=1')
plt.xlabel('87Rb/86Sr',fontsize=18)
plt.ylabel("87Sr/86Sr",fontsize=18)
# plt.ylim()
plt.grid()
plt.tick_params(labelsize=18)
plt.tight_layout()
plt.scatter(x,y)
a = round(cf1[0],4)
b = round(cf1[1],4)
slope = "87Sr/86Sr = " + str(a)+'(87Rb/86Sr) + ' +str(b)
t = round(((np.log(1+a))/ (1.42*10**(-11))/100000000),3)
img = fig_to_base64_img(fig)
return render_template('geochemi/graph.html',img= img ,a=a,b=b,Rb_list=Rb_list,Sr_list=Sr_list,t=t,n=n)
except ValueError:
return render_template('error.html')
モジュールをimport
以下のモジュールをインストール。そのままコピペで良いです。
from flask import Flask, render_template, redirect, request,
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Agg')
import io
import numpy as np
from io import BytesIO
import base64
<form>から受け取ったstrをfloatへ変換と行数のカウント
request.form.getlistの中身がstr(文字列)として認識されているので、float型に変換します。
また、受け取った先でfor文を回すので行数をlen()でカウントしておきます。
Rb_list = request.form.getlist('Rb1')
Sr_list = request.form.getlist('Sr1')
Rb_list = [float(n) for n in Rb_list ]
Sr_list = [float(n) for n in Sr_list ]
n = len(Rb_list)
グラフを作成する(fig)&引き渡す数字を定義
ここはmatplotlib固有のものなので、解説は省略します。
傾きa、切片b、年代tをそれぞれ定義しておます。(16~18行目)
fig = plt.figure(figsize=(15,6))
ax = fig.add_subplot(111)
cf1 = np.polyfit(x,y,1)
func1 = np.poly1d(cf1)
fig, ay = plt.subplots()
ay.invert_yaxis()
plt.plot(x, func1(x),label='d=1')
plt.xlabel('87Rb/86Sr',fontsize=18)
plt.ylabel("87Sr/86Sr",fontsize=18)
plt.grid()
plt.tick_params(labelsize=18)
plt.tight_layout()
plt.scatter(x,y)
a = round(cf1[0],4)
b = round(cf1[1],4)
t = round(((np.log(1+a))/ (1.42*10**(-11))/100000000),3)
figでは表示できないので、base64に変換
定義したfig_to_base64_imgでfigをbase64に変換します。
img = fig_to_base64_img(fig)
def fig_to_base64_img(fig):
"""画像を base64 に変換する。
"""
# png 形式で出力する。
io = BytesIO()
fig.savefig(io, format="png")
# base64 形式に変換する。
io.seek(0)
base64_img = base64.b64encode(io.read()).decode()
計算値と図をgraph.htmlへ引き渡す
render_templateでgeochemi/graph.htmlに計算値と図を引き渡します。
return render_template('geochemi/graph.html',img= img ,a=a,b=b,Rb_list=Rb_list,Sr_list=Sr_list,t=t,n=n)
データ結果出力画面の作成(graph.htmlの作成)
引き渡されたimg、a、b、Rb_list、Sr_list、t、nをそれぞれ以下のようにgraph.htmlで受けます
~~~~省略~~~~
<tbody id="RbSr_table">
{% for i in range(0,n) %}
<tr>
<td> {{ i }}</td>
<td>{{ Sr_list[i] }}</td>
<td>{{ Rb_list[i] }}</td>
</tr>
{% endfor %}
</tbody>
~~~~省略~~~~
<div id="isochron">
<h5>アイソクロン</h5>
<div>
\[
\frac{87Sr}{86Sr} =
{{ b }} + \frac{87Rb}{86Sr} \left({{ a }}\right)
\]
</div>
<br>
<div style="margin-left: 150px;">
<img src="data:image/png;base64,{{ img }}" class="figure-img img-fluid rounded"/>
</div>
</div>
<h5 class="">年代(傾き)</h5>
<div id="a">
\[
\left(e^{λt}-1\right) = {{ a }}
\]
\[
t = {{ t }} 億年
\]
</div>
~~~~省略~~~~
これで完成です。
Anaconda Promptを実行すると、以下のように画面が変わっていきます。
値を入力して、『Create Graph&calculation』をクリック。
以下のようにデータが表示されました。
HerokuにDeploy
最後にHerokuにDeployします。これは、他のサイトに載っているの今回は省略します。簡単な手順だけ書いておきます
- requirements.txtの作成
- Procfileの作成
- Githubにアップデート
- HerokuとGithubを連携
こちらの記事もご参考にしてください。
まとめ
これができれば、一通りの簡単な計算サイトはFlaskで作成できます。
プログラミングの勉強に悩んだら、、
プログラミングの勉強に悩んだら、『Udemy』をオススメします。
オススメのポイント
・全ての講座がオンライン
・リーズナブルな価格で、実用的なスキル
・購入した講座を何度でも見直せる
・プログラミングの講座が豊富