flask sqlalchemyで躓いたこと。

実行環境

  • Raspberry Pi Zero W Rev 1.1

  • Raspberry Pi OS Lite (32bit)(Debian version: 11 (bullseye))

  • ufw 0.36

  • nginx 1.18.0

  • python 3.9.2

  • -Flask 2.3.2

  • -Flask-SQLAlchemy 3.0.3

  • -Jinja2 3.1.2

  • -pyserial 3.5

  • -SQLAlchemy 2.0.13

前提条件

raspiやufw, nginxなどの初期設定が済んでいる。(リバースプロキシ設定など…)



本文

flaskの勉強のために以下のようなコードを作成した

import serial
import sqlite3
from flask import Flask, request, render_template
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy

ser = serial.Serial(
    port='/dev/ttyAMA0',  # Raspberry Pi Zero のシリアルポート名
    baudrate=115200,       # ボーレート
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS
)

app = Flask(__name__)

# データベース作成
db_uri = 'sqlite:///test.db'
app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = True

db = SQLAlchemy(app)


class Comment(db.Model):
    __tablename__ = 'comment'
    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())


db.create_all()

# ホーム画面
@app.route("/")
def index():
    return render_template("index.html")

# 設定画面
@app.route("/settings")
def settings():
    return render_template("settings.html")
# index.htmlに投稿データを渡す

@app.route("/data", methods=['GET'])
def data():
    # テーブルから投稿データをSELECT文で引っ張ってくる
    text = Comment.query.all()
    return render_template("data.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)

# マニュアル画面
@app.route("/manual")
def manual():
    return render_template("manual.html")

# シリアル通信の送信
@app.route("/send_serial/<int:medicine_number>")
def send_serial(medicine_number):
    if medicine_number in [1, 2, 3]:
        # シリアル通信の送信
        ser.write(str(medicine_number).encode())
        print(f"Send serial data: {medicine_number}")
        return 'OK'
    else:
        return 'ERROR'

if __name__ == "__main__":
    app.run("0.0.0.0", debug="True")

flaskやsqlalchemyなどの仕様確認のために組んだコードである。


いろいろなサイトで紹介されているコードは上記のように”db.create_all()”がclassの直後に書かれているが、バージョンが違うためなのかこの記述だと実行できてもOperatingErrorが出るか、実行エラーになる。

書き換えたコード

import serial
import sqlite3
from flask import Flask, request, render_template
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy

ser = serial.Serial(
    port='/dev/ttyAMA0',  # Raspberry Pi Zero のシリアルポート名
    baudrate=115200,       # ボーレート
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS
)

app = Flask(__name__)

# データベース作成
db_uri = 'sqlite:///test.db'
app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = True

db = SQLAlchemy(app)


class Comment(db.Model):
    __tablename__ = 'comment'
    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())

with app.app_context():
    db.create_all()

# ホーム画面
@app.route("/")
def index():
    return render_template("index.html")

# 設定画面
@app.route("/settings")
def settings():
    return render_template("settings.html")
# index.htmlに投稿データを渡す

@app.route("/data", methods=['GET'])
def data():
    # テーブルから投稿データをSELECT文で引っ張ってくる
    text = Comment.query.all()
    return render_template("data.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)

# マニュアル画面
@app.route("/manual")
def manual():
    return render_template("manual.html")

# シリアル通信の送信
@app.route("/send_serial/<int:medicine_number>")
def send_serial(medicine_number):
    if medicine_number in [1, 2, 3]:
        # シリアル通信の送信
        ser.write(str(medicine_number).encode())
        print(f"Send serial data: {medicine_number}")
        return 'OK'
    else:
        return 'ERROR'

if __name__ == "__main__":
    app.run("0.0.0.0", debug="True")

上記のようにdb.create_all()をそのまま書くのではなくwith app.app_context():下に書いてあげると正常に動作する。

# db.create_all()
with app.app_context():
    db.create_all()

app.app_context()を使用することで、アプリケーション固有の機能にアクセスできるコンテキストが作成されます。
これにより、db.create_all()などのデータベース関連の操作をアプリケーションコンテキスト内で使用できるようになります。



この記事が気に入ったらサポートをしてみませんか?