Next.js / Python Flask / SQLite3
前回、PyCharmを使ったスタンドアローンGUIのPythonプログラミンングを試しましたが、今回はこの環境でWebプログラミングをしてみます。
UIをPythonでやることもできますが、やはりJavaScriptの利便性には敵いませんので、今一番よく使われているReactのWebフレームであるNext.jsを使用します。
環境) Next.js v14.0.2 / node v18.12.1, npm(npx) v8.19.2 / Python v3.9.6 / PyCharm 2023.2.4 CE, sqlite3 v3.39.5 / Mac(arm64)
Reactについて過去記事
データベースはSQLite3を使用します。これでWebアプリの最小限の機能を実現できます。
参考) https://zenn.dev/ovrsa/articles/6e66c50f3b3429
今回一番やりたかったことは、FrontEndをnode.jsで動かすのではなく、PythonのサーバにNext.jsのコードを静的ファイルとしてデプロイして動作させることです。そして一歩進んで、実行ファイル化して実行するテストもしました。(コンソール起動、ダブルクリック起動) この時、SQLite3のDBファイルのパスについていろいろと考慮する必要がありました。
app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
import os import sys from flask import Flask, jsonify, request, render_template, send_from_directory from flask_cors import CORS from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() dpath = os.path.dirname(sys.argv[0]) #app = Flask(__name__, static_folder=dpath + '/static') app = Flask(__name__) CORS(app) #app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + dpath + '/demo.db' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///demo.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db.init_app(app) class Item(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(80), nullable=False) def __init__(self, name): self.name = name def __repr__(self): return f'<Item {self.name}>' with app.app_context(): db.create_all() #@app.route('/') #def index(): # return render_template('index.html') ''' @app.route('/templates/<path:path>') def send_file(path): return send_from_directory('templates', path) ''' @app.route('/hello') def hello(): return "<h3>Hello from Flask!</h3>" @app.route('/items', methods=['GET']) def get_items(): items = Item.query.all() return jsonify([str(item) for item in items]) @app.route('/items', methods=['POST']) def add_item(): name = request.json['name'] item = Item(name=name) db.session.add(item) db.session.commit() return jsonify(str(item)), 201 if __name__ == '__main__': app.run(debug=True) |
staticフォルダに下記ビルド結果のoutフォルダの中身をコピーして持ってきます。
実行ファイル化したときの、staticファイルをコマンドで指定するか、コードで明記するかや、dbファイルの場所も、カレントかユーザルートか、を変更するため、コメント部分はあえて残しました。
next.js環境構築
npx create-next-app frontend
cd frontend
npm install
–
pages/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
import { useState, useEffect } from 'react'; export default function Home() { const [items, setItems] = useState([]); const [newItem, setNewItem] = useState(''); useEffect(() => { fetchItems(); }, []); const fetchItems = async () => { const res = await fetch('http://localhost:5000/items'); const data = await res.json(); setItems(data); }; const addItem = async () => { if (!newItem) return; const res = await fetch('http://localhost:5000/items', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ name: newItem }), }); const data = await res.json(); setItems([...items, data]); setNewItem(''); }; return ( <div> <h2>Next.js / Python Flask / SQLite3</h2> <ul> {items.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> <input type="text" value={newItem} onChange={(e) => setNewItem(e.target.value)} /> <button onClick={addItem}>Add Item</button> </div> ); } |
ステートフックと副作用フックのおかげでとてもシンプルにコーディングできることがわかります。
データのあるべき姿を記述すれば、変化があったときに関連する部分が自動的に更新されます。(リアクティブの特徴)
“npm run dev”の実行時、コンフリクトのエラーが出るため、app/page.js を無効にするか、リネームします。
ビルド設定
next.config.js
1 2 3 4 5 6 |
/** @type {import('next').NextConfig} */ const nextConfig = { basePath: "/static", output: 'export' } |
ビルド実行
npx next build
–
outフォルダに生成されるファイルをapp.pyと同階層にあるstaticフォルダにコピーします。
nodejsで実行(一応確認のため)
npm run dev
–
デフォルトでは、staticは不要ですが、ここではexport時にPythonが静的ファイルとしてアクセスするためのPathと合わせるためにこのようになっています。
Flaskサーバ実行
python app.py
–
app.pyと同階層のinstanceフォルダに、demo.dbが作成されます。
誤解のないように補足いたします。
上記二つとも同じ内容を表示していますが、複製していますので、ソースは違います。(データベースは同じもの)またFlaskサーバのみで実行できます。Nodejsサーバで内容を確認するときはFlaskサーバの起動が必要です。
AddItemボタンでデータを追加した結果のDB
% sqlite3 demo.db
sqlite> .mode column
sqlite> .table
item
sqlite> .schema
CREATE TABLE item (
id INTEGER NOT NULL,
name VARCHAR(80) NOT NULL,
PRIMARY KEY (id)
);
sqlite> select * from item;
id name
— —-
1 aaaa
2 bbbb
3 cccc
4 1111
sqlite>
Pythonファイルの実行形式化
pyinstaller -F –add-data “static:static” –onefile –clean app.py
–
distフォルダに 実行ファイルapp が出力されます。–noconsole オプションをつけると起動時コンソールが表示されなくなり、出力ファイルのMacのパッケージになリ、このときDBファイルはパッケージの中に書かれます。Finderからダブルクリック起動の場合は、カレントでなくユーザルートに書かれます。
(最後にWeb画面に表示されたデータが思ったものと違ったので確認しました。PythonのQueryで取得したまま表示されています。
今回はここまでとします。)
Category: 未分類