詳細Flask(動画と書籍のイイとこ取り)
1 はじめに
Flaskの学習を始めると、Youtubeや有料の動画学習サイト、紙や電子書籍など様々な選択肢があります。しかし、動画学習は、コード入力時、一旦停止する必要があったり、書籍については、コードのコピペができなかったりと、無駄な作業や確認が発生し、時間をかけた割に、全く進まず、ストレスばかりたまる状態でした。このような非効率な状況を改善するとともに、Youtubなどでは、あまり触れられていない、DB、Form、認証(login)、権限制御についても詳細に解説した資料を作成しました。
本資料の強みは、動画学習と書籍学習のイイとこ取り!!
動画サイトの様に、コード入力時、動画を停止する必要なし。
コードは、全て、コピペでOK(実際に入力して方が良いが、時間が無い方向け)。
Flaskで利用する、DB、Form、認証(login)、権限制御について、これでもか!という程、詳細に解説。
1-2 対象者
Pythonの文法を一通り学習し、Webアプリを作りたい方。
HTML/CSSの入門程度を理解できている方。
YoutubのFlaskに関する動画で学習経験のある方。
Youtub等の動画でFlaskの学習を行ったが、もっと詳細にFlaskを学習したい方。
動画学習だと、イチイチ停止することになるのが、煩わしい方。
本講義は、かなりのボリュームとなっているため、根気よく取り組める方。
1-3 環境
今回の環境は、以下の環境で実施しております。利用するモジュール等のバージョンが変更になった場合についての動作は未確認となっておりますのでご了承ください。
【Base環境】
OS:Windows10 Home
開発環境:anaconda利用(anacondaで仮想環境作成)
python version:3.10.12
【使用ライブラリとバージョン】
Flask 2.0.3
Flask-Login 0.5.0
Flask-Migrate 3.1.0
Flask-SQLAlchemy 2.5.1
Flask-WTF 0.15.1
Jinja2 3.0.3
pytz 2021.3
SQLAlchemy 1.4.44
Werkzeug 2.0.3
wheel 0.38.4
WTForms 2.3.3
1-4 注意事項
【注意事項-1】
本資料は、HTML/CSSの講座ではないため、HTML/CSSについての解説はございませんのでご了承ください。なお、画面の装飾については、BootStrap5を利用しております。
【注意事項-2】
本資料は、DB、Form、認証(login)、権限制御 に力を入れておりますので、2 Flask入門、3 Templateの利用については、簡単な説明となっておりますのでご了承ください。
【注意事項-3】
本ページ内で、目次に記載している項目全ての解説は行いません。(膨大な量となるため)
4 FlaskのForm詳細からは、説明も詳細になりコードも長くなるため別ページに分けて解説します。
2 Flask入門
2-1 Flaskの基本
【フォルダ構成】
./
│──app.py→新規作成
Flaskでは、pythonコード内に、HTMLコードを記載し、ブラウザで表示させることができます。
app.pyというファイルを作成し、以下コードを実行してください。
実行方法は、app.pyファイルを作成したディレクトリに移動し、コマンドプロンプト(ターミナル)から以下のコマンドを実行してください。 コマンド実行後、http://127.0.0.1:5000/にブラウザでアクセスし、helloと表示されれば成功です。
実行が終わったら、コマンドプロンプトで、Ctrl+Cでアプリケーションを終了してください。
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>hello</h1>'
if __name__ == '__main__':
app.run()
2-2 ルーティング
2つの異なるURL(接続先のWebページ)があり、それぞれのURLへのアクセスに対して異なるHTML応答を返します。http://127.0.0.1:5000/は "hello" というメッセージを表示し、http://127.0.0.1:5000/testは "hello Test Page!" というメッセージを表示します。再度、python app.pyを実行し動作を確認してください。そして、実行が終わったら、コマンドプロンプトで、Ctrl+Cでアプリケーションを終了してください。
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>hello</h1>'
@app.route('/test')
def test():
return '<h1>hello Test Page!</h1>'
if __name__ == '__main__':
app.run()
2-3 動的変更
'/user/<user_id>'という形式のURLを定義しています。<user_id>はダイナミックな部分で、実際のユーザーIDに置き換えられます。例えば、'/user/123'にアクセスした場合、Flaskはuserid()関数を呼び出し、user_id引数にはURLの一部として提供されたユーザーID(この場合は123)が渡されます。関数はそのユーザーIDをHTMLメッセージに埋め込み、それをブラウザに返します。つまり、このURLはユーザーIDに応じて異なるメッセージを表示します。
再度、python app.pyを実行し動作を確認してください。そして、実行が終わったら、コマンドプロンプトで、Ctrl+Cでアプリケーションを終了してください。
【確認方法】
ブラウザでは、以下へアクセス
http://127.0.0.1:5000/user/123
http://127.0.0.1:5000/user/456
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>hello</h1>'
@app.route('/test')
def test():
return '<h1>hello Test Page!</h1>'
@app.route('/user/<user_id>')
def userid(user_id):
return '<h1>User ID: {0}</h1>'.format(user_id)
if __name__ == '__main__':
app.run()
2-4 デバッグモードによる起動
これまで、コードを変更するたびに、
コードを保存→Ctrl+Cでアプリを終了→python app.pyによるアプリの再実行を行ってきました。毎回、上記を行うと、非常に非効率なので、デバッグモードでアプリを起動することで、Ctrl+Cでアプリを終了→python app.pyをコード変更のたびに実施しなくてよくなります。
デバッグモードでの起動方法は、コード内に、app.run(debug=True) と記載するだけで、今までと同様にpython app.pyで起動すればOKです。
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>hello</h1>'
@app.route('/test')
def test():
return '<h1>hello Test Page!</h1>'
@app.route('/user/<user_id>')
def userid(user_id):
return '<h1>User ID: {0}</h1>'.format(user_id)
if __name__ == '__main__':
# デバッグモードで起動
app.run(debug=True)
3 Templateの利用
3-1 Templateの基本
【フォルダ構成】
./
│──app.py
│──templates(新規作成)
│──index.html(新規作成)
Flaskでは、実際のWebページは、pythonのコード内にHTMLを記載するのではなく、ブラウザに表示するためのHTMLファイルだけを別途作成しておきます。HTMLファイルの作成場所は、app.pyファイルと同階層に、tempatesというフォルダを作成し、その中にHTMLファイルを保存します。(ここでは、index.htmlを作成)
先ほどまでは、pythonコード(app.py)内に、HTMLも記載していましたが、実際に、pythonコード内にWebページを表示するHTMLを記載すると、
コードが長くなり複雑になってしまいます。そのため、処理を行うコード(python)と画面表示を行うコードを分けて作成します。そのため、テンプレートという機能を利用します。
テンプレートを利用するには、まず、app.py内にrender_templateをimportします。そして、view関数内のreturn文に、
render_template('呼び出したいHTMLファイル名')という形式で記載を行います。ここでは、http://127.0.0.1:5000/にアクセスすると、index.htmlが呼び出され、Hello Templateとブラウザに表示されます。
# app.py
from flask import Flask, render_template
app = Flask(__name__)
# view関数
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
<! -- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Hello Template</h1>
</body>
</html>
3-2 Templateの共有利用・共通化
【フォルダ構成】
./
│──app.py
│──templates
│──base.html(新規作成)
│──index.html(修正)
実際のWebアプリでは、複数のページを作成しますが、共有で利用する部分があります。例えば、ヘッター、フッター、メニューなど。これらのコードを全てのページに記載していては、非常に非効率となります。そのため、共通で利用できる部分については、baseとなるひな形を利用し使いまわしを行います。そして、共通で利用する部分以外には、各ページの内容をページごとに読み込めるようにしておきます。
まず、templateフォルダ内に、base.htmlというファイルを作成します。
そして、ボディ部に {% block content %} {% endblock %}を記載します。
<! -- base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Template継承</title>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
次に、先ほどまで利用していたindex.htmlを以下の様に変更します。
重要なのは、先頭に、{% extends "base.html" %}を先頭に記載すること、そして、 {% block content %} {% endblock %}を記載することです。
{% extends "base.html" %}を記載することで、base.htmlを継承し、headタグ内のtitleなどもタブに表示されるようになります。また、index.html内の
{% block content %} {% endblock %}の間に記載した内容は、base.html内の {% block content %} {% endblock %}の間に埋め込まれて表示されるようになります。
<! -- index.html -->
{% extends "base.html" %}
{% block content %}
<h1>Hello Template</h1>
<p>base template継承中</p>
{% endblock %}
# app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
それでは、app.pyを実行し、http://127.0.0.1:5000/にアクセスしてみてください。タブに「Hello Template」と表示され、ブラウザに「Hello Template」「base template継承中」が表示されれば、成功です。
3-3 Jinja2の利用
【フォルダ構成】
./
│──app.py(修正)
│──templates
│──base.html
│──index.html(修正)
FlaskでHTML内でPythonの処理や変数等の表示を行いたい場合、jinjaというテンプレートエンジンを利用します。
以下の例では、python側で設定した変数をindex.html側で表示する例を示しています。なお、テンプレート内で、if文やfor文等の使用方法については、「4 FlaskのForm詳細」で適宜使用しますので、現時点での詳細は割愛します。
return render_template('index.html', user_name=user_name)の
左側のuser_nameは、index.html内に渡される変数(名)となるため、
index.htmlの変数名と同じにする必要があります。
# app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
# 変数をHTMLで表示
user_name = "YAMADA TARO"
return render_template('index.html', user_name=user_name)
@app.route('/test')
def test():
return '<h1>hello Test Page!</h1>'
@app.route('/user/<user_id>')
def userid(user_id):
return '<h1>User ID: {0}</h1>'.format(user_id)
if __name__ == '__main__':
app.run(debug=True)
<! -- index.html -->
{% extends "base.html" %}
{% block content %}
<! -- app.pyから渡された変数を表示 -->
<h1>変数は {{user_name}}</h1>
{% endb<lock %}
3-4 url_for()関数の利用
【フォルダ構成】
./
│──app.py
│──templates
│──base.html(修正)
│──index.html
Webアプリでページ遷移を行う場合に、url_for('view関数名')関数を利用します。
この関数を利用すると、URLが変更になっても、リンクに関する修正を伴わないので非常に効率的な管理ができます。
以下のコードでは、よくある、ナビゲーションのメニューにあるHomeへ移動する場合の書き方を示しています。
<! -- base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<!-- JavaScript Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"></script>
<title>SAMPLE</title>
</head>
<body>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<!-- url_for()使用 -->
<a class="nav-link active" href="{{url_for('index')}}">Home</a>
</li>
</ul>
</div>
</div>
</nav>
{% block content %}
{% endblock %}
</body>
</html>
<!-- index.html -->
{% extends "base.html" %}
{% block content %}
{% endblock %}
# app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/test')
def test():
return '<h1>hello Test Page!</h1>'
@app.route('/user/<user_id>')
def userid(user_id):
return '<h1>User ID: {0}</h1>'.format(user_id)
if __name__ == '__main__':
app.run(debug=True)
3-5 エラーページ対応
【フォルダ構成】
./
│──app.py(修正)
│──templates
│──error_pages(新規作成)
│──404.html(新規作成)
│──base.html
│──index.html
Webページにアクセスした際、存在しないページにアクセスする可能性もあります。その時に、エラーを返すページを以下の様に作成します。
エラーは、存在しないページにアクセスする場合以外にも、多々ありますが、ここでは、入門として、一番簡単なエラーページの表示方法を示します。権限が無い場合のページにアクセスした場合のエラー等については、
「4 FlaskのForm詳細」で随時解説致します。
<!-- 404.html -->
{% extends "base.html" %}
{% block content %}
<section id="menu">
<div class="container my-3 py-4 bg-light">
<div class="row">
<div class="col-md-3">
<a href="{{url_for('index')}}" class="btn btn-secondary w-100">メインへ</a>
</div>
</div>
</div>
</section>
<header id="page-header">
<div class="container my-3 py-3 bg-light">
<h1>ページが見つかりません</h1>
<p>誠に恐れ入ります。</p>
</div>
</header>
{% endblock %}
# app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
# エラーページ処理
@app.errorhandler(404)
def error_404(error):
return render_template('error_pages/404.html'), 404
if __name__ == '__main__':
app.run(debug=True)
app.pyを起動し、http://127.0.0.1:5000/sample など、存在しないページにアクセスしてみてください。
4 FlaskのForm詳細
4-1 Fromの作成
4-2 view関数の作成
4-3 base.html(ベーステンプレート)の編集
4-4 register.html(ユーザー登録フォーム)の編集
4-5 maintenance.html(ユーザーメンテナンスページ)の編集
4-6 Formの入力チェック
4-7 Flashメッセージの表示
4-8 エラーメッセージの表示(Formクラスの編集)
4-9 エラーメッセージの表示(エラー用マクロの作成)
4-10 エラーメッセージの表示(各種テンプレートの編集)
4-11 Fromの修正(装飾)
5 データベースの基礎
5-1 データベースの作成方法(テータベースの定義)
5-2 データベースの作成方法(テーブルの定義)
5-3 データベースの作成方法(データベースとテーブルの作成)
5-4 テーブルの基本操作
5-5 Flask Migrateの使い方
5-6 リレーション設定(外部キーの設定)
5-7 外部キー制約有効化の設定
5-8 外部キー制約の動作確認
5-9リレーション状況の確認
6 データベース実践
6-1 データベースの作成
6-2 データベースへの登録(ユーザーの登録)
6-3 データベースからのデータ取得 100
6-4 登録情報の重複チェック
6-5 ページネーション(view関数の編集)
6-6 ページネーション(現在ページのデーター表示)
6-7 ページネーション(前へ・次への実装)
6-8 ページネーション(ページ番号の実装)
6-9 登録データの更新(更新用フォーム追加)
6-10 登録データの更新(更新用view関数追加)
6-11 登録データの更新(更新用テンプレートの編集)
6-12 ユーザーメンテナンスページの修正・変更
6-13 入力チェック機能の追加
6-14 登録データの削除機能の追加
7 PWハッシュ化とFlask-login(ログイン認証)
7-1 werkzeugの使い方とPWハッシュ化
7-2 PWハッシュ化とユーザー登録
7-3 Flask-loginの機能について
7-4 ログインマネージャーの設定
7-5 Userモデルの変更
7-6 ログイン用フォームの追加
7-7 ログイン用view関数の追加
7-8 ログイン用テンプレートの変更
7-9 ログインしているユーザーの表示
7-10 ユーザー登録時のパスワードハッシュ化
7-11 パスワード更新時のパスワードのハッシュ化
7-12 ログアウト機能
8 権限による制御方法
8-1 ログインユーザー以外からの保護方法(login_required)
8-2 登録データの更新権限設定
8-3 アクセス権限が無い場合に表示されるエラーページの対応
8-4 ユーザ登録の制限設定
8-5 ユーザー更新の制限設定
8-6 ユーザー削除の権限設定
この記事が気に入ったらサポートをしてみませんか?