[前編] 文系初学者がPython×Flask×データベースをつかって検温記録WEBアプリをつくってみたメモ
この記事は、忘れっぽい自分のための覚え書きメインですが、初学者/文系/非エンジニア/趣味レベルの同胞にも参考になればと、丁寧めに書き留めていきます。
ただ、鬼のように長くなったので2つに分けました。(それでも長いです)
↓↓ 後編はコチラ ↓↓
ちなみにこの時からアップデートを重ね、いまはLINEでサクッと記録できるようになりました。一般公開してますのでよろしければお試しください。
PCと言語の環境
■ macOS Catalina10
■ Python3.7.7
■ Flask
制作当時の参考文献は3.7系が多かったので、3.8までは上げませんでした。
PythonのWEBフレームワークとしてはDjangoが有名ですが、やりたいことと習得レベル的に手軽ぽいFlaskを選択しました。文献はDjangoの方が豊富ですが、ここはシンプルさ重視。
なお筆者はいまだに「フレームワーク?何それテクノ神?」状態ですが、ざっくり概念は下の動画がわかりやすかったです。
その他つかったサービス
■ コード書くのに「VS Code」
ググったら割と新しめの記事で推されてた印象。つかったらエディタどころじゃなくいろいろできて便利でした。(それでも1割も使い切れてないと思う)
■ ファイルの管理に「Github」
よくわからんけど、有名だし使ってみたかった。純粋な憧れ。
■ WEBを公開するため「Heroku」
これまたよくわからんけど、有名なので文献も多いかなと。
■ データベース管理は「Heroku Postgres」
Herokuで制作していくと、行きがかり上これになりました。データベースはカンペキに無知無学のゼロスタートです。
1.プロジェクト用のフォルダを作る
これから色々とファイルをつくって保存していく親フォルダを作ります。どこでもいいですが、iCloud内じゃないい方がよさげです。(このあとターミナルを操作するときに、iCloudDriveはパスの指定がややこしかった)
おすすめはローカルにあるユーザーフォルダの下あたり。ターミナル操作にせよFinderから開くにせよ、アクセスしやすいです。
筆者はユーザーフォルダ'kazuyamano'の下に、'Dev'という開発もろもろ用の親フォルダをつくって、その下に'ken-on-kun'というプロジェクト個別の子フォルダを作りました。(日々の体温を記録するWEBアプリ=「検温くん」)
*この時点では、フォルダ内はスッカラカンでいいです。焦らずいきます。
2.ターミナルで初期作業もろもろ
Gitを設定
ターミナルで、さっき作ったPJTフォルダへ移動(change directory)。gitのローカルリポジトリとして設定(git init) 。
1行ずつ実行します。
% cd ~/Dev/ken-on-kun
% git init
特に何も起きません。エラーが起きなければ成功です。
環境を仮想化(venv)
このプロジェクト「ken-on-kun」内での作業が、他のプロジェクトやPC全体に影響を与えないように、仮想環境を作成(vertual enviroment) 。
内側から結界張るようなイメージ?
わからなくてもいいです。1行ずつ実行します。
% python3 -m venv venv
% source venv/bin/activate
うまくいったら、ターミナルの行頭に(venv)って表示が入ってきます。
(venv) フォルダ名 %
↓ 筆者の実例
(venv) kazuyamano@MacBookPro ken-on-kun %
ここまでやると「ken-on-kun」の下に「venv」フォルダが作られ、venv環境に必要と思しきファイル群が追加されています。
*中身はよくわからんけど気にせずすすみます。
Flaskをインストール
ひきつづきターミナルで作業。
pipをアップグレードします。
*pip=Pythonの便利なパッケージをインストールするためのツール。
$ pip install --upgrade pip
成功したら、バージョンを確認
$ pip -V
つづいてPythonの便利なパッケージ(フレームワーク)、その名もFlaskをインストールします。
$ pip install flask
↓一連の作業をすると、こんな感じになります
字!字!
--upgrade pipも、install flaskも、'Successfully installed ●●' って出てるのでどっちも成功してます。
3.VSCodeで初期作業もろもろ
VS Codeを開いて、[ファイル] → [開く.…] → [ken-on-kun] を開きます。
↑ ターミナルと並べて作業しがち。便利です ↑
この時点で、VSCode左メニューのgit管理アイコン(枝分かれ的なやつ)に、未読バッチみたいなのが860件とかついてビビります。まだ何もしてないのに!
これはさっき追加された「venv」フォルダ内のファイル数を表していて(たぶん)、「新しいファイルが860件追加されたよ!」と通知が働いた模様(たぶん)。
とりあえず、ken-on-kunの下に「.gitignore」というファイルをつくって、中に'venv'と平文で打ち込んで保存しましょう。860件の通知が消えます(厳密には「.gitignoreってファイルが追加されたよ!」の通知1件に変わります)
gitはバージョン管理の仕組みなのでほっとくとあらゆる変更履歴を拾って通知してくれるのですが、「.gitignore」ファイルをつくってそこにフォルダ名を登録すると、例外的に変更履歴と見なさない決まりのようです。(たぶん)
*[venv]フォルダの中は環境設定のためのファイル群で、これから開発するプログラムそのものとは関係ない。だからignore = 無視していいよ、ということのようです(しらんけど)。
いよいよプログラムをつくっていきます
Flaskの公式ドキュメントにクイックスタートやチュートリアルもありますし、ググれば立派なエンジニアさんのちゃんとした文献もモリモリ出てきます。正しい知識・詳しい情報は、そっち見てもらった方がいいです。
ただ、エンジニアリング界隈の予備知識がない我々にはハードル高く心折れがちなので、このnoteでは「理解するよりも、前に進めたいマジで」の精神で、必要な作業となんとなくの意味合いを、初心者目線で記録していきます。
[Special Thanks文献]
全体の流れはこちらのチュートリアルを完コピさせていただいてます。これがなかったら無理でした。本当にありがたい。インターネット最高。
4.VSCodeでひと通りファイルとフォルダを基礎工事
~/run.py
ken-on-kunの下に[run.py]ファイルを追加。コードは以下。
from main import app
if __name__ == '__main__':
app.run(debug=True)
完了したら、VS Code上の見た目はこんな感じ
って意味わかんないですよね。わからなくてもとりあえず大丈夫なので、心を無にして写経上等で突き進みましょう。
*以降こんな感じで、初心者的 ”わかってないけど自分なりの解釈” をメモっていきます。正しい知識や解説は、公式ドキュメントやエンジニア系文献ググってください。
~/main/__init__.py
ken-on-kunの下に[main]フォルダを新設(↓のあたり右クリックで作れます)
mainの下に[__init__.py]ファイルを追加。
from flask import Flask
app = Flask(__name__)
import main.views
run.pyの1行目「from main」を受けて、mainフォルダ内にある__init__.pyって名前のファイルが走る、で「import app」の'app'に当たるものが、__init__.pyの中で定義されている、という感じかと。
このあたり難しいですが、Flaskの基本的なお作法っぽいので、ひとつひとつの意味を深く考えず、一旦「そういうもんだな」で進んだ方がいいと思います。
_init_.pyについて掘り下げるならコチラなど。
~/main/views.py
mainの下に[views.py]ファイルを追加。
import flask
from main import app
@app.route('/')
def show_entries():
return 'Hello, World!'
@app.route()のくだりは「ルーティング」といって、URLアクセス時の動きを定めるパーツのようです。()内にURLの末尾部分を指定します。'/' はいわゆるトップページのことですね。
例)http://www.hogehoge.jp/ ←この最後の'/'を表してる
[テスト] ローカルで実行、ブラウザで確認
ターミナルでrun.pyを実行
$ python run.py
実際のターミナル画面こんな感じです
Running on http://127.0.1:5000/ って書いてあるので、ブラウザでアクセスしてみると
はい、成功です。わーい
ちなみに「自分のPC」っていうIPアドレスを表す時のお決まりとして
・127.0.0.1
・0.0.0.0
・localhost
3通りあるみたいです。試しに打ち込んだら全部Helloしてくれました。
勉強なりました。以下参考文献。
*ターミナルがPython実行モードなので、ctrl+Cで戻しておきましょう
5.データベースの下準備
引き続きこちらのサイトを参考に進めます
サイトではブログを題材に「タイトル」と「本文」をDB化しますが、こちらは検温記録アプリなので少しアレンジして「誰」「いつ」「何℃」をDBに記録するWEBアプリを目指します。
flask_sqlalchemyをインストール
$ pip install flask_sqlalchemy
データベースまわりはムズイのでまずは実践で進みます。参考文献。
~/main/__init__.py を編集
インストールしたSQLALchemyを、基礎工事プログラムに組み込んでいきます。
from flask import Flask
from flask_sqlalchemy import SQLAlchemy #ここ追加
app = Flask(__name__)
app.config.from_object('main.config') #ここ追加
db = SQLAlchemy(app) #ここ追加
import main.views
~/main/config.py (DBの配置とか設定)
mainの下に[config.py]ファイルを追加。
import os
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or "sqlite:///test.db"
SQLALCHEMY_TRACK_MODIFICATIONS = True
SECRET_KEY="secret key"
だそうです。むずい。次いってみよー
~/main/models.py (どんなDBか設計)
mainの下に[models.py]ファイルを追加。
よく出てくるチュートリアルは”ブログを作ろう”で「連番=id」「タイトル=title」「本文=text」を定義していますが(下記)
from main import db
from flask_sqlalchemy import SQLAlchemy
class Entry(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.Text)
text = db.Column(db.Text)
def __repr__(self):
return "<Entry id={} title={!r}>".format(self.id, self.title)
def init():
db.create_all()
今回は”検温を記録”なので、「連番=id」「誰=jcode」「何℃=temp」「いつ=date」という感じでアレンジしてみます。(jcodeは従業員番号)
from main import db
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, timedelta, timezone
class Entry(db.Model):
id = db.Column(db.Integer, primary_key=True)
jcode = db.Column(db.String)
temp = db.Column(db.Float)
date = db.Column(db.DateTime, default=datetime.now())
def __repr__(self):
return "<Entry id={} jcode={!r} temp={!r} date={}>".format(self.id, self.jcode, self.temp, self.date)
def init():
db.create_all()
3,8,9行目のdatetimeまわりは、データが作られた日時が日本時間で自動登録されるよう仕込んでします。なかなか思うようにデータ反映されずいろんなやり方を試行錯誤しましたが、ここに落ち着きました。
__repr__と{!r}の意味がわからなくて調べましたが、なんとなくの理解まで。
わからないなりに以下サイトが参考になりました。
6.データベースをつくる
ターミナルで以下実行します。
% python -c "import main.models; main.models.init()"
~/main/config.pyで設定した
この指定場所にデータベースが作成されます。今回はローカルでの実行なので 'or' 以降が適用され、~/mainフォルダの下にtest.dbが生成されているはずです。
test.dbできてました。成功です。ちなみにVSCodeでは中身は見れません。
[テスト] 箱にデータを入れてみる
ターミナルを「インタラクティブシェル」モードにします
$ python
>>>が出れば成功です。(ctrl+Dで戻れます)
>>> from main.models import Entry
>>> from main import db
>>> entry1 = Entry(jcode='1111111', temp='36.1')
>>> db.session.add(entry1)
>>> db.session.commit()
>>> entry2 = Entry(jcode='2222222', temp='36.2')
>>> db.session.add(entry2)
>>> db.session.commit()
>>> entries = Entry.query.all()
>>> entries
上記を1行ずつEnterしていきます。.pyファイルのコードを1行ずつ実行する感じですね。
最後の2行、2件のデータが配列として取り出され、entry1・2としてハンド追加したデータがちゃんとDBに収まってたことがわかります。deteもちゃんと作業時間が入ってます。テスト成功!
* [ctrl+D] で、インタラクティブシェルモードを終了させておきましょう
7.ブラウザにDBを表示する
さっき基礎工事したプログラムを、データベースとつないでいきます。
~/main/views.py を編集 (その1)
Before:世界に呼びかける壮大なプロジェクトですが、それだけです。
After:データベースの中身をトップページに表示します。データが変われば、トップページも変わる=動的サイトになります。
import flask
from main import app
from main.models import Entry # 追加
@app.route('/')
def show_entries():
entries = Entry.query.all() # 追加
return flask.render_template('entries.html', entries=entries) # 変更
上2つは、 さきほどインタラクティブシェルでやったことの移植。3つめは、 flask.render_templateという仕組みを使って.pyファイルから.htmlファイルを呼び出してトップページに表示します。
最終的には、トップページに検温履歴を表示する機能につながります。
~/main/templates
flask.render_templateという仕組みでは、[templates]というフォルダに入っている.htmlファイルを 'テンプレート' と認識して呼び出すきまりのようです。
なので、とりあえず[templates]フォルダをつくります。場所はviews.pyと同列になるよう、[main]フォルダの下です。
~/main/templates/entries.html
[templates]フォルダの下に、entries.htmlを作ります。さきほどのviews.pyの最後に呼び出してた、トップページの表示内容を決めるhtmlファイルです。
<html>
<head>
<title>検温くん_第一形態</title>
</head>
<body>
<ul class="entries">
{% for entry in entries %}
<li>社員コード:{{entry.jcode}} / 体温:{{entry.temp}} / 検温日時:{{entry.date}}</li>
{% endfor %}
</ul>
</body>
</html>
20年前に阿部寛ばりのHPを手書きしてた身としては { } ってのが違和感たっぷりですが、見るとfor文が入ってたりするので、Pythonのコードを埋めて動的なサイトにする'テンプレート'ということなのかと思います(たぶん)
views.pyで「テーブルからデータ全件抽出するクエリを追加→変数'entries'に代入」を行っているので、
{% for entry in entries %}
でひとつずつデータを取り出し
<li>{{entry.jcode}} / {{entry.temp}} / {{entry.date}}</li>
でリスト表示する感じですね。
[テスト] ローカルで実行、ブラウザで確認
ターミナルでrun.pyを実行
% python run.py
Running on http://127.0.1:5000/ なので、ブラウザでアクセスしてみます。
キター! さっき追加したデータがちゃんと表示されました。成功です。
*ターミナルがPython実行モードなので、ctrl+Cで戻しておきましょう
8.ブラウザからDBに書き込む
いよいよブラウザからデータベースに書き込む仕掛けをつくっていきます。WEBアプリっぽい。動的感出てきます。
~/main/views.py を編集 (その2)
import flask
from main import app, db #インポート対象にdbを追加
from main.models import Entry
@app.route('/')
def show_entries():
entries = Entry.query.all()
return flask.render_template('entries.html', entries=entries)
# DBにデータを追加するadd_entry関数を定義
@app.route('/add', methods=['POST'])
def add_entry():
entry = Entry(jcode = flask.request.form['jcode'],temp = flask.request.form['temp'])
db.session.add(entry)
db.session.commit()
return flask.redirect(flask.url_for('show_entries'))
~/main/templates/entries.html を編集
<html>
<head>
<title>検温くん_第二形態</title>
</head>
<body>
<br>
<h1>検温結果おしえてクリクリ</h1>
<form action="{{ url_for('add_entry') }}" method=post>
<p>社員コードは? <input type="text" name="jcode" maxlength="7"> ※半角英数字7桁で!</p>
<p>今朝の体温は? <input type="text" name="temp" maxlength="4"> ※半角数字○○.○で!</p>
<p><input type="submit" value="送るのねん"></p>
</form>
<br>
<p>[送信履歴]</p>
<ul class="entries">
{% for entry in entries %}
<li>社員コード:{{entry.jcode}} / 体温:{{entry.temp}} / 検温日時:{{entry.date}}</li>
{% endfor %}
</ul>
</body>
</html>
いったん見栄えをチェックしましょう。ターミナルで
% python run.py
http://127.0.1:5000/ にブラウザでアクセス。
いい感じぶぁい。
[テスト] 入力フォームからデータを送信
(履歴が増えてますが気にしないで下さい)
適当にフォームに入れて送ってみます。えいっ!
わーい。
見た目はさて置き、ローカル環境ではひと通り機能ができあがりました。
(補記)
なお検温日時は送信した時間じゃなく、ローカルサーバーの起動時間になってました。ターミナルで[ctrl+C]でサーバー切らない限り、何回送っても同じ時間が記録されました。仕様としてはイマイチですが、実用としては1日に1・2回しか使わないサービスなので、本番環境なら毎回セッション切れててサーバ起動するので問題ないかなと。いったん割り切って進みます。
つづきは後編へ
ここまでで11,619文字・・・長い・・・
ここからいよいよインターネットの世界に飛び出していきますが、これ以降は別の記事に分けます。
Git、Github、Heroku、Heroku Postgresなど使うツールも増えますし、トラブル・バグも多発&ググっても答えが見つからず・・・途方に暮れることが多くなるところですが、引きつづき実例主義で「とにかく進めたい」のお手伝いができればと思います。
ここまで読んでいただいた皆さま、まずはありがとうございましたm(_ _)m
↓↓後編はコチラ ↓↓
この時からアップデートを重ねて、いまではLINEでサクッと記録できるようになりました。一般公開してますのでよろしければお試しください。
この記事が気に入ったらサポートをしてみませんか?