参照先
Installation — Flask Documentation (3.0.x)
Installation
Create an environment
% pwd
~/Desktop/guri_flask/myproject
$ mkdir myproject
$ cd myproject
$ python3 -m venv .venv
Activate the environment
$ . .venv/bin/activate
Install Flask
$ pip install Flask
Collecting Flask
Downloading flask-3.0.3-py3-none-any.whl.metadata (3.2 kB)
Collecting Werkzeug>=3.0.0 (from Flask)
Downloading werkzeug-3.0.3-py3-none-any.whl.metadata (3.7 kB)
Collecting Jinja2>=3.1.2 (from Flask)
Downloading jinja2-3.1.4-py3-none-any.whl.metadata (2.6 kB)
Collecting itsdangerous>=2.1.2 (from Flask)
Downloading itsdangerous-2.2.0-py3-none-any.whl.metadata (1.9 kB)
Collecting click>=8.1.3 (from Flask)
Downloading click-8.1.7-py3-none-any.whl.metadata (3.0 kB)
Collecting blinker>=1.6.2 (from Flask)
Downloading blinker-1.8.2-py3-none-any.whl.metadata (1.6 kB)
Collecting MarkupSafe>=2.0 (from Jinja2>=3.1.2->Flask)
Downloading MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl.metadata (3.0 kB)
Downloading flask-3.0.3-py3-none-any.whl (101 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 101.7/101.7 kB 1.0 MB/s eta 0:00:00
Downloading blinker-1.8.2-py3-none-any.whl (9.5 kB)
Downloading click-8.1.7-py3-none-any.whl (97 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 97.9/97.9 kB 548.9 kB/s eta 0:00:00
Downloading itsdangerous-2.2.0-py3-none-any.whl (16 kB)
Downloading jinja2-3.1.4-py3-none-any.whl (133 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 133.3/133.3 kB 557.8 kB/s eta 0:00:00
Downloading werkzeug-3.0.3-py3-none-any.whl (227 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 227.3/227.3 kB 435.1 kB/s eta 0:00:00
Downloading MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl (18 kB)
Installing collected packages: MarkupSafe, itsdangerous, click, blinker, Werkzeug, Jinja2, Flask
Successfully installed Flask-3.0.3 Jinja2-3.1.4 MarkupSafe-2.1.5 Werkzeug-3.0.3 blinker-1.8.2 click-8.1.7 itsdangerous-2.2.0
[notice] A new release of pip is available: 24.0 -> 24.2
[notice] To update, run: pip install --upgrade pip
Flaskのバージョン確認
$ flask --version
Python 3.12.4
Flask 3.0.3
Werkzeug 3.0.3
Webサーバーを構築
プロジェクト用にワークスペースを作成
myprojectディレクトリ内にapp.pyを作成
Quickstartを参考にWebサーバーを構築
Quickstart — Flask Documentation (3.0.x)
①プログラム作成
$ cat app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
if __name__ == "__main__":
app.run()
②プログラムの実行
- Flaskを実行するために環境変数を設定
仮想環境をactivate化した状態で
$ export FLASK_APP=app
$ export FLASK_ENV=development
$ export FLASK_DEBUG=1
$ flask run
* Serving Flask app 'app'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 244-907-730
$ export FLASK_DEBUG=1
がない場合は以下。
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
127.0.0.1 - - [12/Aug/2024 17:39:28] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [12/Aug/2024 17:39:28] "GET /favicon.ico HTTP/1.1" 404 -
(確認するには…
$ echo $FLASK_APP
app
$ echo $FLASK_ENV
development
http://127.0.0.1:5000
にアクセスすると
Routing
URLごとに違うページに遷移させる。
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
@app.route("/guri")
def hello_guri():
return "<p>Hello, Guri!</p>"
@app.route("/gura")
def hello_gura():
return "<p>Hello, Gura!</p>"
if __name__ == "__main__":
app.run()
可変URL
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
@app.route("/<name>")
def hello(name):
return f"<p>Hello, {name}!</p>"
if __name__ == "__main__":
app.run()
HTMLファイルを返す
URLごとのHTMLファイルを返す。(別の画面を表示)
$ cat app.py
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/")
def hello_world():
return render_template('index.html')
if __name__ == "__main__":
app.run()
Top画面作成
base.htmlを作成
今後、Flaskで画面作成する際のベース(雛形)となるテンプレートとしてbase.htmlを作成する。
templatesディレクトリ内にbase.htmlを作成する。
```base.html```
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>メモアプリ</title>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
```index.html```
{% extends "base.html" %}
{% block content %}
<h1>メモアプリ</h1>
<article>
<h2>メモ1</h2>
<p>メモ1の内容</p>
</article>
<article>
<h2>メモ2</h2>
<p>メモ2の内容</p>
</article>
{% endblock %}
```app.py```
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/")
def top():
return render_template('index.html')
if __name__ == "__main__":
app.run()
app.py側でメモリストを受け取り、画面側に出力する機能の追加
```app.py```
from flask import Flask
from flask import render_template
app = Flask(__name__)
memo_list = [
{'title':"test01", 'body':"ぐり主任です。"},
{'title':"test02", 'body':"ぐらです。"}
]
@app.route("/")
def top():
return render_template('index.html', memo_list=memo_list)
if __name__ == "__main__":
app.run()
```index.html```
{% extends "base.html" %}
{% block content %}
<h1>メモアプリ</h1>
{% if memo_list == [] %}
<p>メモは登録されていません</p>
{% else %}
{% for memo in memo_list %}
<article>
<h2>{{ memo.title }}</h2>
<p>{{ memo.body }}</p>
</article>
{% endfor %}
{% endif %}
{% endblock %}
top画面を表形式で表示させるように修正
```index.html```
{% extends "base.html" %}
{% block content %}
<h1>メモアプリ</h1>
{% if memo_list == [] %}
<p>メモは登録されていません</p>
{% else %}
<table border="1">
<tr>
<th>タイトル</th>
<th>メモ</th>
</tr>
{% for memo in memo_list %}
<tr>
<td>{{ memo.title }}</td>
<td>{{ memo.body }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endblock %}
DBを使用したtop画面の作成
DB(:DataBase)に接続
$ pwd
/Users/koji/Desktop/guri_flask/myproject
% sqlite3 flaskmemo.db
SQLite version 3.43.2 2023-10-10 13:08:14
Enter ".help" for usage hints.
sqlite>
Ctrl+Cキーで接続解除できる。
テーブルを作成
DBに接続した状態で、テーブルを作成。
$ sqlite3 flaskmemo.db
SQLite version 3.43.2 2023-10-10 13:08:14
Enter ".help" for usage hints.
sqlite> CREATE TABLE memo(
(x1...> id INTEGER PRIMARY KEY AUTOINCREMENT,
(x1...> title TEXT NOT NULL,
(x1...> body TEXT NOT NULL
(x1...> );
sqlite>
作成済みのテーブル一覧を表示して確認。
sqlite> .tables
memo
.tablesで今定義しているテーブルの一覧を表示する。
memoというテーブル名で作成したものを確認。
また、.schemaでテーブルの構造(schema)を表示する。
sqlite> .schema
CREATE TABLE memo(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
body TEXT NOT NULL
);
CREATE TABLE sqlite_sequence(name,seq);
INSERTでレコードを作成する
sqlite> INSERT INTO memo (title, body)
...> VALUES ("First message", "Hello world");
sqlite> INSERT INTO memo (title, body)
...> VALUES ("Second message", "Hello Guri");
UPDATEでレコードを更新する
DBに接続した状態で、テーブル一覧表示して確認。
$ sqlite3 flaskmemo.db
SQLite version 3.43.2 2023-10-10 13:08:14
Enter ".help" for usage hints.
sqlite> .tables
memo
テーブル(テーブル名がmemo)のレコードを確認する。
sqlite> SELECT * FROM memo;
1|First message|Hello World
2|Second message|Hello Guri
3|Third message|Hello Gura
続いて、テーブル(テーブル名がmemo)のレコードを更新する。
qlite> UPDATE memo
...> SET body="Hello World!"
...> WHERE id=1;
テーブル(テーブル名がmemo)のレコードを確認する。
sqlite> SELECT * FROM memo;
1|First message|Hello World!
2|Second message|Hello Guri
3|Third message|Hello Gura
DELETEでテーブルからレコードを削除
DBに接続。
$ sqlite3 flaskmemo.db
SQLite version 3.43.2 2023-10-10 13:08:14
Enter ".help" for usage hints.
sqlite>
テーブル一覧を表示。
sqlite> .tables
memo
テーブルの構造を表示。
sqlite> .schema
CREATE TABLE memo(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
body TEXT NOT NULL
);
CREATE TABLE sqlite_sequence(name,seq);
テーブル名がmemoのテーブルのレコードを表示。
sqlite> SELECT * FROM memo;
1|First message|Hello World!
2|Second message|Hello Guri
3|Third message|Hello Gura
id=3のレコードを削除してみる。
sqlite> DELETE FROM memo
...> WHERE id=3;
# 確認
sqlite> SELECT * FROM memo;
1|First message|Hello World!
2|Second message|Hello Guri
BEGIN; ROLLBACK;あるいはBEGIN; COMMIT;でトランザクション処理
top画面でDBを利用した実装
```app.py```
from flask import Flask
from flask import render_template, g
import sqlite3
DATABASE = "flaskmemo.db"
app = Flask(__name__)
@app.route("/")
def top():
memo_list = get_db().execute("SELECT id, title, body FROM memo").fetchall()
return render_template('index.html', memo_list=memo_list)
# DBと接続
def connect_db():
con = sqlite3.connect(DATABASE)
con.row_factory = sqlite3.Row
return con
def get_db():
if not hasattr(g, 'sqlite_do'):
g.sqlite_db = connect_db()
return g.sqlite_db
if __name__ == "__main__":
app.run()
top画面から登録画面への遷移
top画面に新規登録のボタンを追加。
```app.py```
{% extends "base.html" %}
{% block content %}
<h1>メモアプリ</h1>
<a href="/regist" role="button">新規登録</a>
{% if memo_list == [] %}
<p>メモは登録されていません</p>
{% else %}
<table border="1">
<tr>
<th>タイトル</th>
<th>メモ</th>
</tr>
{% for memo in memo_list %}
<tr>
<td>{{ memo.title }}</td>
<td>{{ memo.body }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endblock %}
登録画面へのルーティングを追加。
```app.py```
from flask import Flask
from flask import render_template, g
import sqlite3
DATABASE = "flaskmemo.db"
app = Flask(__name__)
@app.route("/")
def top():
memo_list = get_db().execute("SELECT id, title, body FROM memo").fetchall()
return render_template('index.html', memo_list=memo_list)
@app.route("/regist", methods=['GET', 'POST'])
def regist():
return render_template('regist.html')
# DBと接続
def connect_db():
con = sqlite3.connect(DATABASE)
con.row_factory = sqlite3.Row
return con
def get_db():
if not hasattr(g, 'sqlite_do'):
g.sqlite_db = connect_db()
return g.sqlite_db
if __name__ == "__main__":
app.run()
登録画面regist.htmlを作成
```regist.html```
{% extends "base.html" %}
{% block content %}
<h1>新規登録</h1>
<form method="POST">
<table border="1">
<tr>
<th>タイトル</th>
<th>メモ</th>
</tr>
<tr>
<td>
<input type="text" name="title">
</td>
<td>
<input type="text" name="body">
</td>
</tr>
</table>
<input type="submit" value="新規登録">
</form>
<a href="/" role="buttun">一覧へ戻る</a>
{% endblock %}
新規登録画面で新規登録機能の実装
index.html
{% extends "base.html" %}
{% block content %}
<h1>メモアプリ</h1>
<a href="/regist" role="button">新規登録</a>
{% if memo_list == [] %}
<p>メモは登録されていません</p>
{% else %}
<table border="1">
<tr>
<th>タイトル</th>
<th>メモ</th>
</tr>
{% for memo in memo_list %}
<tr>
<td>{{ memo.title }}</td>
<td>{{ memo.body }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endblock %}
regist.html
{% extends "base.html" %}
{% block content %}
<h1>新規登録</h1>
<form method="POST">
<table border="1">
<tr>
<th>タイトル</th>
<th>メモ</th>
</tr>
<tr>
<td>
<input type="text" name="title">
</td>
<td>
<input type="text" name="body">
</td>
</tr>
</table>
<input type="submit" value="新規登録">
</form>
<a href="/" role="buttun">一覧へ戻る</a>
{% endblock %}
app.py
from flask import Flask
from flask import render_template, g, redirect, request
import sqlite3
DATABASE = "flaskmemo.db"
app = Flask(__name__)
@app.route("/")
def top():
memo_list = get_db().execute("SELECT id, title, body FROM memo").fetchall()
return render_template('index.html', memo_list=memo_list)
@app.route("/regist", methods=['GET', 'POST'])
def regist():
if request.method == 'POST':
# 画面からの登録情報の取得
title = request.form.get('title')
body = request.form.get('body')
# DBに登録
db = get_db()
db.execute('INSERT into memo (title, body) VALUES (?, ?)', [title, body])
db.commit()
return redirect('/')
return render_template('regist.html')
# DBと接続
def connect_db():
con = sqlite3.connect(DATABASE)
con.row_factory = sqlite3.Row
return con
def get_db():
if not hasattr(g, 'sqlite_do'):
g.sqlite_db = connect_db()
return g.sqlite_db
if __name__ == "__main__":
app.run()
メモの編集画面を作成
top画面(index.html)の編集
新しいカラムを右に追加。
{% extends "base.html" %}
{% block content %}
<h1>メモアプリ</h1>
<a href="/regist" role="button">新規登録</a>
{% if memo_list == [] %}
<p>メモは登録されていません</p>
{% else %}
<table border="1">
<tr>
<th>タイトル</th>
<th>メモ</th>
<th></th>
</tr>
{% for memo in memo_list %}
<tr>
<td>{{ memo.title }}</td>
<td>{{ memo.body }}</td>
<td>
<a href="/{{memo.id}}/edit" role="button">編集</a>
</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endblock %}
Python側(app.py)を編集
from flask import Flask
from flask import render_template, g, redirect, request
import sqlite3
DATABASE = "flaskmemo.db"
app = Flask(__name__)
@app.route("/")
def top():
memo_list = get_db().execute("SELECT id, title, body FROM memo").fetchall()
return render_template('index.html', memo_list=memo_list)
@app.route("/regist", methods=['GET', 'POST'])
def regist():
if request.method == 'POST':
# 画面からの登録情報の取得
title = request.form.get('title')
body = request.form.get('body')
# DBに登録
db = get_db()
db.execute('INSERT into memo (title, body) VALUES (?, ?)', [title, body])
db.commit()
return redirect('/')
return render_template('regist.html')
@app.route("/<id>/edit", methods=['GET', 'POST'])
def edit(id):
if request.method == 'POST':
# 画面からの登録情報の取得
title = request.form.get('title')
body = request.form.get('body')
# DBに登録
db = get_db()
db.execute('UPDATE memo SET title=?, body=? WHERE id=?', [title, body, id])
db.commit()
return redirect('/')
post = get_db().execute(
"SELECT id, title, body FROM memo WHERE id=?", (id, )
).fetchone()
return render_template('edit.html', post=post)
# DBと接続
def connect_db():
con = sqlite3.connect(DATABASE)
con.row_factory = sqlite3.Row
return con
def get_db():
if not hasattr(g, 'sqlite_do'):
g.sqlite_db = connect_db()
return g.sqlite_db
if __name__ == "__main__":
app.run()
編集画面(edit.html)の作成
{% extends "base.html" %}
{% block content %}
<h1>編集</h1>
<form method="POST">
<table border="1">
<tr>
<th>タイトル</th>
<th>メモ</th>
</tr>
<tr>
<td>
<input type="text" name="title" value="{{post.title}}">
</td>
<td>
<input type="text" name="body" value="{{post.body}}">
</td>
</tr>
</table>
<input type="submit" value="編集">
</form>
<a href="/" role="buttun">一覧へ戻る</a>
{% endblock %}
削除画面の作成
トップ画面(index.html)を修正
削除ボタンを追加。
{% extends "base.html" %}
{% block content %}
<h1>メモアプリ</h1>
<a href="/regist" role="button">新規登録</a>
{% if memo_list == [] %}
<p>メモは登録されていません</p>
{% else %}
<table border="1">
<tr>
<th>タイトル</th>
<th>メモ</th>
<th></th>
</tr>
{% for memo in memo_list %}
<tr>
<td>{{ memo.title }}</td>
<td>{{ memo.body }}</td>
<td>
<a href="/{{memo.id}}/edit" role="button">編集</a>
<a href="/{{memo.id}}/delete" role="button">削除</a>
</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endblock %}
Python側の修正①(delete.htmlを返す機能のみ実装)
from flask import Flask
from flask import render_template, g, redirect, request
import sqlite3
DATABASE = "flaskmemo.db"
app = Flask(__name__)
@app.route("/")
def top():
memo_list = get_db().execute("SELECT id, title, body FROM memo").fetchall()
return render_template('index.html', memo_list=memo_list)
@app.route("/regist", methods=['GET', 'POST'])
def regist():
if request.method == 'POST':
# 画面からの登録情報の取得
title = request.form.get('title')
body = request.form.get('body')
# DBに登録
db = get_db()
db.execute('INSERT into memo (title, body) VALUES (?, ?)', [title, body])
db.commit()
return redirect('/')
return render_template('regist.html')
@app.route("/<id>/edit", methods=['GET', 'POST'])
def edit(id):
if request.method == 'POST':
# 画面からの登録情報の取得
title = request.form.get('title')
body = request.form.get('body')
# DBに登録
db = get_db()
db.execute('UPDATE memo SET title=?, body=? WHERE id=?', [title, body, id])
db.commit()
return redirect('/')
post = get_db().execute(
"SELECT id, title, body FROM memo WHERE id=?", (id, )
).fetchone()
return render_template('edit.html', post=post)
@app.route("/<id>/delete", methods=['GET', 'POST'])
def delete(id):
post = get_db().execute(
"SELECT id, title, body FROM memo WHERE id=?", (id, )
).fetchone()
return render_template('delete.html', post=post)
# DBと接続
def connect_db():
con = sqlite3.connect(DATABASE)
con.row_factory = sqlite3.Row
return con
def get_db():
if not hasattr(g, 'sqlite_do'):
g.sqlite_db = connect_db()
return g.sqlite_db
if __name__ == "__main__":
app.run()
削除画面(delete.html)の作成
{% extends "base.html" %}
{% block content %}
<h1>削除</h1>
<p>以下のメモを削除しますか?</p>
<form method="POST">
<table border="1">
<tr>
<th>タイトル</th>
<th>メモ</th>
</tr>
<tr>
<td>
<input type="text" name="title" value="{{post.title}}" disabled>
</td>
<td>
<input type="text" name="body" value="{{post.body}}" disabled>
</td>
</tr>
</table>
<input type="submit" value="削除">
</form>
<a href="/" role="buttun">一覧へ戻る</a>
{% endblock %}
Python側のapp.pyの編集②(削除ボタンで削除機能を追加
from flask import Flask
from flask import render_template, g, redirect, request
import sqlite3
DATABASE = "flaskmemo.db"
app = Flask(__name__)
@app.route("/")
def top():
memo_list = get_db().execute("SELECT id, title, body FROM memo").fetchall()
return render_template('index.html', memo_list=memo_list)
@app.route("/regist", methods=['GET', 'POST'])
def regist():
if request.method == 'POST':
# 画面からの登録情報の取得
title = request.form.get('title')
body = request.form.get('body')
# DBに登録
db = get_db()
db.execute('INSERT into memo (title, body) VALUES (?, ?)', [title, body])
db.commit()
return redirect('/')
return render_template('regist.html')
@app.route("/<id>/edit", methods=['GET', 'POST'])
def edit(id):
if request.method == 'POST':
# 画面からの登録情報の取得
title = request.form.get('title')
body = request.form.get('body')
# DBに登録
db = get_db()
db.execute('UPDATE memo SET title=?, body=? WHERE id=?', [title, body, id])
db.commit()
return redirect('/')
post = get_db().execute(
"SELECT id, title, body FROM memo WHERE id=?", (id, )
).fetchone()
return render_template('edit.html', post=post)
@app.route("/<id>/delete", methods=['GET', 'POST'])
def delete(id):
if request.method == 'POST':
# 画面から登録情報の取得
db = get_db()
db.execute('DELETE FROM memo WHERE id=?', (id,))
db.commit()
return redirect('/')
post = get_db().execute(
"SELECT id, title, body FROM memo WHERE id=?", (id, )
).fetchone()
return render_template('delete.html', post=post)
# DBと接続
def connect_db():
con = sqlite3.connect(DATABASE)
con.row_factory = sqlite3.Row
return con
def get_db():
if not hasattr(g, 'sqlite_do'):
g.sqlite_db = connect_db()
return g.sqlite_db
if __name__ == "__main__":
app.run()
ログイン画面の作成
flask-loginライブラリのインストール。
$ pip install flask-login
確認。
$ pip list | grep Flask
Flask 3.0.3
Flask-Login 0.6.3
コメント