よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

PythonとFlaskでつくる簡易掲示板

PythonのWebフレームワークFlaskを使ってローカルで動く簡易掲示板をつくります。
ほぼ写経です。Flaskの動作確認とsqlalchemyとの初対面の記録。

参考リンク

flask_thread_app/app.py at master · beginerSE/flask_thread_app · GitHub
【Python】Flask+SQLAlchemyで「ひとこと掲示板」を作る
参考にさせて頂きました。ありがとうございます。


実行風景


投稿フォームと、投稿内容

f:id:chayarokurokuro:20191112020415j:plain


投稿したらこの画面に

f:id:chayarokurokuro:20191112020535j:plain



前々回前回、投稿をファイルに保存するWebアプリをbottleを使って作りましたが、それと基本的に変わりません。
htmlのテンプレートで入力フォームの<textarea>を配置し、サーバー側にPOST送信。bottleがそれを裏方で処理する流れ。
ですが今回はbottleに替わってFlask、そして慣れないSQLが使われています。sqlalchemyという何か得体の知れないものも登場しています。初対面です。



結果的に、BottleとFlaskの違いは今回はよく分かりませんでした。テンプレートエンジンが違うぐらいで書き方は大差ないのかな。bottleにないメソッドや機能がFlaskにはあるんでしょうけど使ってませんので何とも言えない…
今回のアプリケーションの肝はWeb云々Flask云々というよりSQLの処理。コードの行数的にも多くを占めている。SQLはWebフレームワークの学習と切り離して別の機会でやった方がいいな。混乱のもとかも。
はい、話を先に進めます。

目次


実行環境


Windows10
Anaconda
Python3.7


Androidスマホ
termux
Python3.7


動作確認

FlaskはAndroidスマホでも動作確認しました。 termuxから通常通り pip install flask で。



flaskのインストール


仮想環境をactivateして、Flaskをインストール。

conda install flask

または

pip install flask

など。

Successfully installed Werkzeug-0.16.0 click-7.0 flask-1.1.1 itsdangerous-1.1.0


flask以外のものも自動的にインストールされた。



flask_sqlalchemyのインストール


初対面のsqlalchemy。何者かよくわからない。
Pythonの為のORM(オブジェクト・リレイショナル・マッピング)ライブラリだとある。データベースをオブジェクト指向プログラミングで行うテクニックで、メンテナンス性だとかが向上するみたいなことが書いてある。とりあえず分からんがスッ飛ばしてインストール。

pip install flask_sqlalchemy

Successfully installed SQLAlchemy-1.3.10 flask-sqlalchemy-2.4.1


SQLAlchemyも一緒にインストールされた。



作るもの


  1. プロジェクトフォルダflask_app
  2. templatesフォルダ(テンプレート保存フォルダ)
  3. 実行用Pythonファイル
  4. index.html(投稿フォームと内容出力)
  5. result.html(投稿完了)
flask_app/
 |-- app.py
 |-- templates
 |   |-- index.html
 |   `-- result.html
 `-- test.db(自動作成)



実行と終了方法


実行はターミナルから。プロジェクトフォルダに作業ディレクトリを移動して

python app.py


  1. 実行するとサーバーが起動します。
  2. ブラウザでhttp://localhost:5000/にアクセスすると掲示板のページが出ます。
  3. フォームから内容と名前を書いて送信ボタンを押せば、
  4. 投稿はプロジェクトフォルダ内に自動作成されたsqliteデータベースファイルに保存されます。
  5. そのデータは http://localhost:5000/ にアクセスする度に読み込まれ、入力フォーム下に表示されます。
  6. 終了はターミナルでCtrl+cキーを入力するとサーバーが止まります。


投稿データはプロジェクトフォルダ内に自動作成されたtest.dbに保存される。
これをファイルごと削除すれば投稿履歴も消えてゼロからスタート。同ファイルは再作成される。



1.プロジェクトフォルダflask_appの作成


今回作るフォルダやファイルを入れておく為のフォルダを適当に作る。フォルダの名前は何でもOKです。flask_appとしました。



2.テンプレート保存用フォルダtemplates作成(要注意)


ファルダ名に要注意!!
htmlで書いたテンプレートファイルは、プロジェクトフォルダ下にtemplatesの名で作ったフォルダに保存しなければならないようです。
flaskの内部に使われているテンプレートエンジンJinja2の決まりだったかな。テンプレートファイルを他のフォルダに保存してパスを指定しても読み込んでくれない。



3.実行用Pythonファイルapp.py作成


1.プロジェクトフォルダに保存

# ライブラリのインポート
from flask import Flask, request, render_template
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy

# Flaskクラスのインスタンス化
app = Flask(__name__)

# オブジェクト変更追跡システム無効設定
SQLALCHEMY_TRACK_MODIFICATIONS = False
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# SQLAlchemyでデータベースに接続する
db_uri = 'sqlite:///test.db'
app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
db = SQLAlchemy(app)


class Comment(db.Model):
    """[テーブルの定義を行うクラス]
    Arguments:
        db {[Class]} -- [ライブラリで用意されているクラス]
    """

    id_ = db.Column(db.Integer, primary_key=True, autoincrement=True)
    pub_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    name = db.Column(db.Text())
    comment = db.Column(db.Text())

    def __init__(self, pub_date, name, comment):
        """[テーブルの各カラムを定義する]
        [Argument]
            id_ -- 投稿番号(プライマリキーなので、自動で挿入される)
            pub_date -- 投稿日時
            name -- 投稿者名
            comment -- 投稿内容
        """

        self.pub_date = pub_date
        self.name = name
        self.comment = comment


try:
    db.create_all()
except Exception as e:
    print(e.args)
    pass

# index.htmlに投稿データを渡す
@app.route("/")
def index():
    # テーブルから投稿データをSELECT文で引っ張ってくる
    text = Comment.query.all()
    return render_template("index.html", lines=text)

# 投稿の送信とデータベース追加
@app.route("/result", methods=["POST"])
def result():
    # 現在時刻 投稿者名 投稿内容を取得
    date = datetime.now()
    comment = request.form["comment_data"]
    name = request.form["name"]
    # テーブルに格納するデータを定義する
    comment_data = Comment(pub_date=date, name=name, comment=comment)
    # テーブルにINSERTする
    db.session.add(comment_data)
    # テーブルへの変更内容を保存
    db.session.commit()
    return render_template("result.html", comment=comment, name=name, now=date)


# 実行
if __name__ == "__main__":
    app.run(host="localhost", debug=True)
    



4.index.html作成


2.で作ったtemplatesフォルダに保存。

<!--DOCTYPE html-->
<html lang="ja">
<meta charset="utf-8">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>簡易掲示板</title>
</head>
<body>
      <h1>【掲示板】</h1>
      <form action="/result" method="post">
          <input type="hidden" name="comment_box">
          <label for="comment_">投稿</label>
          <textarea name="comment_data" rows="6" cols="50"></textarea>
          <p></p>
          <label for="name">名前</label>
          <input type="text" name="name">
          <button type="submit">送信する</button>
          </form>
      <h2>【投稿一覧】</h2>
      
      <form method="post" action="">
         <table class="table">
              <thead class="thead-dark">
                  <tr>
                      <th>日付</th>
                      <th>名前</th>
                      <th>投稿内容</th>
                      </tr>
                  </thead>
              <tbody>
                  {% for th in lines %}
                  <tr>
                      <td>{{ th.pub_date }}</td>
                      <td>{{ th.name }}</td>
                      <td>{{ th.comment }}</td>
                      </tr>
                  {% endfor %}
                  </tbody>
              </table>
          </form>
      
     <p></p>
</body> 
</html>



5.result.html作成


これも同じく2.作ったtemplatesフォルダに保存

<!--DOCTYPE html-->
<html lang="ja">
<meta charset="utf-8">
<head>
    <title>書き込み完了</title>
</head>
<body>
    <h1>コメントを書き込みました</h1>
    <br>
    <form action="/" method="get">
        <button type="submit">戻る</button>
    </form>
</body>
</html>



備考


きになるところをいくつか。

templatesフォルダ
フォルダの命名が固定化されているようなので、この名前を付ける。



実行Pythonファイルapp.py
写経元のコードはrun()メソッドでhostもportも指定がない。その他のFlaskのサイトにも指定がない。デフォルトでlocalhostの5000番っぽい。

コードはSQLの読み書きで占められている。長い。pandasとか使ってやる方が短くて済むんじゃないの?と素人考えで思うがsqlalchemyの学習が必要そうなので飛ばす。実はSQLも満足に使えないのだけど・・・
余談だがsqlalchemyの使用はSqlite以外は必須になったんだという記事を見た。

SQLALCHEMY_TRACK_MODIFICATIONS = Falseとあるが、なんだろう?
python - How do I know if I can disable SQLALCHEMY_TRACK_MODIFICATIONS? - Stack Overflow

Flask-SQLAlchemyには階層化された独自のイベント通知システムがある。これを行うためにSQLAlchemyへ変更を追跡するが、余分なリソースが必要になるのでこの追跡システムのオプションを無効にすることができる。
デフォルトの設定はSQLALCHEMY_TRACK_MODIFICATIONS = True。無効にするにはFalse。云々
なんのこっちゃ。



テンプレートファイル index.html result.html
テンプレートファイルはtemplatesフォルダに入れること。

おわりに


FlaskがAndroidとWindows10で動くことは確認できた。SQLとsqlalchemyの使い方の課題もできた。
Flask関連記事はBottleと違って実際にアプリをサーバー上で運用する前提で書いてあったりする。
アドレスの設定やコンピュータ同士の接続、暗号技術の知識も要りますね。世界が拡がってまいります。

以上です。