著者は、 Write for DOnations プログラムの一環として、 Free and Open SourceFundを選択して寄付を受け取りました。
序章
テキストフィールドやテキスト領域などのWebフォームを使用すると、ユーザーは、アプリケーションがアクションを実行するために使用するドロップダウンまたはラジオボタンであるかどうかにかかわらず、アプリケーションにデータを送信したり、大量のテキストをに送信したりすることができます。処理または表示されます。 たとえば、ソーシャルメディアアプリケーションでは、ユーザーがページに新しいコンテンツを追加できるボックスをユーザーに提供できます。
Flask は、Python言語でWebアプリケーションを作成するための便利なツールと機能を提供する軽量のPythonWebフレームワークです。 Flaskで安全かつ柔軟な方法でWebフォームをレンダリングおよび検証するには、 Flask-WTF を使用します。これは、FlaskでWTFormsライブラリを使用するのに役立つFlask拡張機能です。応用。
WTForms は、柔軟なWebフォームレンダリングを提供するPythonライブラリです。 これを使用して、テキストフィールド、テキスト領域、パスワードフィールド、ラジオボタンなどをレンダリングできます。 WTFormsは、さまざまなバリデーターを使用した強力なデータ検証も提供します。これにより、ユーザーが送信するデータが、定義した特定の基準を満たしていることが検証されます。 たとえば、必須フィールドがある場合、ユーザーが送信するデータが提供されていること、または特定の長さであることを確認できます。
WTFormsはまた、CSRFトークンを使用して、 CSRF攻撃からの保護を提供します。これは、攻撃者がユーザーが認証されたWebアプリケーションで不要なアクションを実行できるようにする攻撃です。 CSRF攻撃が成功すると、ユーザーは、銀行アプリケーションで攻撃者の銀行口座に資金を送金したり、ユーザーの電子メールアドレスを変更したりするなど、状態を変更する要求を実行するように強制される可能性があります。 被害者が管理者アカウントである場合、CSRFはWebアプリケーション全体を危険にさらす可能性があります。
このチュートリアルでは、Flask-WTFを使用してWebフォームをレンダリングおよび検証する方法を示す小さなWebアプリケーションを作成します。 アプリケーションには、Pythonリストに保存されているコースを表示するためのページがあり、インデックスページには、コースのタイトル、説明、価格、可用性、およびレベル(初級、中級、または上級)を入力するためのフォームがあります。
前提条件
- ローカルのPython3プログラミング環境。 Python3シリーズのローカルプログラミング環境をインストールおよびセットアップする方法」のオペレーティングシステムのチュートリアルに従ってください。 このチュートリアルでは、プロジェクトディレクトリ
flask_app
を呼び出します。 - ルート、ビュー関数、テンプレートなどの基本的なFlaskの概念の理解。 Flaskに慣れていない場合は、FlaskとPythonを使用して最初のWebアプリケーションを作成する方法およびFlaskアプリケーションでテンプレートを使用する方法を確認してください。
- 基本的なHTMLの概念の理解。 背景知識については、HTMLを使用してWebサイトを構築する方法チュートリアルシリーズを確認できます。
- (オプション)Flaskでの基本的なWebフォームの使用法の理解。 FlaskアプリケーションでWebフォームを使用する方法を参照してください。
ステップ1—FlaskとFlask-WTFのインストール
このステップでは、FlaskとFlask-WTFをインストールします。これにより、WTFormsライブラリも自動的にインストールされます。
仮想環境をアクティブにした状態で、pip
を使用してFlaskとFlask-WTFをインストールします。
pip install Flask Flask-WTF
インストールが正常に完了すると、出力の最後に次のような行が表示されます。
OutputSuccessfully installed Flask-2.0.2 Flask-WTF-1.0.0 Jinja2-3.0.3 MarkupSafe-2.0.1 WTForms-3.0.0 Werkzeug-2.0.2 click-8.0.3 itsdangerous-2.0.1
ご覧のとおり、WTFormsライブラリもFlask-WTFパッケージの依存関係としてインストールされました。 残りのパッケージはFlaskの依存関係です。
必要なPythonパッケージをインストールしたので、次にWebフォームを設定します。
ステップ2—フォームの設定
このステップでは、WTFormsライブラリからインポートするフィールドと検証ツールを使用してWebフォームを設定します。
次のフィールドを設定します。
- タイトル:コースタイトルのテキスト入力フィールド。
- 説明:コース説明のテキスト領域フィールド。
- 価格:コースの価格の整数フィールド。
- レベル:初級、中級、上級の3つの選択肢があるコースレベルの無線フィールド。
- 利用可能:コースが現在利用可能かどうかを示すチェックボックスフィールド。
まず、flask_app
ディレクトリにあるforms.py
という新しいファイルを開きます。 このファイルには、アプリケーションで必要なフォームが含まれています。
nano forms.py
このファイルには、Webフォームを表すクラスが含まれます。 上部に次のインポートを追加します。
フラスコ_app/forms.py
from flask_wtf import FlaskForm from wtforms import (StringField, TextAreaField, IntegerField, BooleanField, RadioField) from wtforms.validators import InputRequired, Length
Webフォームを作成するには、FlaskForm
基本クラスのサブクラスを作成します。これは、flask_wtf
パッケージからインポートします。 また、wtforms
パッケージからインポートするフォームで使用するフィールドを指定する必要があります。
次のフィールドをWTFormsライブラリからインポートします。
- StringField :テキスト入力。
- TextAreaField :テキスト領域フィールド。
- IntegerField :整数のフィールド。
- BooleanField :チェックボックスフィールド。
- RadioField :ユーザーが選択できるラジオボタンのリストを表示するためのフィールド。
from wtforms.validators import InputRequired, Length
行で、フィールドで使用するバリデーターをインポートして、ユーザーが有効なデータを送信することを確認します。 InputRequired は、入力が提供されていることを確認するために使用するバリデーターです。 Length は、文字列の長さを検証して、文字列の最小文字数を確認するためのものです。特定の長さを超えない。
次に、import
ステートメントの後に次のclassを追加します。
フラスコ_app/forms.py
class CourseForm(FlaskForm): title = StringField('Title', validators=[InputRequired(), Length(min=10, max=100)]) description = TextAreaField('Course Description', validators=[InputRequired(), Length(max=200)]) price = IntegerField('Price', validators=[InputRequired()]) level = RadioField('Level', choices=['Beginner', 'Intermediate', 'Advanced'], validators=[InputRequired()]) available = BooleanField('Available', default='checked')
ファイルを保存して閉じます。
このCourseForm
クラスでは、前にインポートしたFlaskForm
基本クラスから継承します。 WTFormsライブラリからインポートしたフォームフィールドを使用して、フォームフィールドのコレクションをクラス変数として定義します。 フィールドをインスタンス化する場合、最初の引数はフィールドのラベルです。
wtforms.validators
モジュールからインポートするバリデーターのリストを渡すことにより、各フィールドのバリデーターを定義します。 たとえば、タイトルフィールドには、ラベルとして文字列'Title'
と、次の2つのバリデータがあります。
InputRequired
:フィールドが空であってはならないことを示します。Length
:2つの引数を取ります。min
は10
に設定され、タイトルの長さが10文字以上になるようにします。また、max
は100
に設定され、そうでないことを確認します。 100文字を超えます。
説明テキスト領域フィールドには、InputRequired
バリデーターと、max
パラメーターが200
に設定されたLength
バリデーターがあり、[の値はありません。 X158X]パラメータ。これは、200文字を超えないことが唯一の要件であることを意味します。
同様に、price
と呼ばれるコースの価格に必要な整数フィールドを定義します。
level
フィールドは、複数の選択肢がある無線フィールドです。 Pythonリストで選択肢を定義し、それをchoices
パラメーターに渡します。 また、InputRequired
バリデーターを使用して、必要に応じてフィールドを定義します。
available
フィールドはチェックボックスフィールドです。 default
パラメーターに渡すことにより、デフォルトの'checked'
値を設定します。 これは、ユーザーがチェックを外さない限り、新しいコースを追加するときにチェックボックスがオンになることを意味します。つまり、コースはデフォルトで利用可能です。
WTFormsライブラリの使用方法の詳細については、WTFormsドキュメントのクラッシュコースページを参照してください。 その他のフィールドについてはフィールドページを、フォームデータを検証するためのその他のバリデーターについてはバリデーターページを参照してください。
forms.py
ファイルでWebフォームを構成しました。 次に、Flaskアプリケーションを作成し、このフォームをインポートして、そのフィールドをインデックスページに表示します。 また、別のページにコースのリストを表示します。
ステップ3—Webフォームとコースを表示する
このステップでは、Flaskアプリケーションを作成し、前のステップで作成したWebフォームをインデックスページに表示し、コースのリストとその上にコースを表示するためのページも作成します。
プログラミング環境をアクティブにしてFlaskをインストールしたら、flask_app
ディレクトリ内で編集するためにapp.py
というファイルを開きます。
nano app.py
このファイルは、必要なクラスとヘルパーをFlaskからインポートし、CourseForm
をforms.py
ファイルからインポートします。 コースのリストを作成し、フォームをインスタンス化してテンプレートファイルに渡します。 次のコードをapp.py
に追加します。
フラスコ_app/app.py
from flask import Flask, render_template, redirect, url_for from forms import CourseForm app = Flask(__name__) app.config['SECRET_KEY'] = 'your secret key' courses_list = [{ 'title': 'Python 101', 'description': 'Learn Python basics', 'price': 34, 'available': True, 'level': 'Beginner' }] @app.route('/', methods=('GET', 'POST')) def index(): form = CourseForm() return render_template('index.html', form=form)
ファイルを保存して閉じます。
ここでは、Flaskから以下をインポートします。
Flask
クラスは、Flaskアプリケーションインスタンスを作成します。- インデックステンプレートをレンダリングする
render_template()
関数。 redirect()
関数は、新しいコースが追加されたときにユーザーをコースページにリダイレクトします。- URLを構築するための
url_for()
関数。
最初にCourseForm()
クラスをforms.py
ファイルからインポートし、次にapp
というFlaskアプリケーションインスタンスを作成します。
Webフォームを保護するためにCSRFトークンを生成するときに使用するWTFormsの秘密鍵構成を設定します。 秘密鍵は長いランダムな文字列である必要があります。 秘密鍵を取得する方法の詳細については、FlaskアプリケーションでWebフォームを使用する方法のステップ3を参照してください。
次に、courses_list
という辞書のリストを作成します。このリストには、現在'Python 101'
というタイトルのサンプルコースを持つ1つの辞書があります。 ここでは、デモの目的でPythonリストをデータストアとして使用します。 実際のシナリオでは、SQLiteなどのデータベースを使用します。 データベースを使用してコースのデータを保存する方法については、FlaskアプリケーションでSQLiteデータベースを使用する方法を参照してください。
index()
ビュー機能のapp.route()
デコレータを使用して、/
メインルートを作成します。 methods
パラメーターでGET
とPOST
HTTPメソッドの両方を受け入れます。 GETメソッドはデータを取得するためのものであり、POSTリクエストは、たとえばWebフォームを介してサーバーにデータを送信するためのものです。 詳細については、FlaskアプリケーションでWebフォームを使用する方法を参照してください。
Webフォームを表すCourseForm()
クラスをインスタンス化し、インスタンスをform
という変数に保存します。 次に、render_template()
関数の呼び出しを返し、index.html
というテンプレートファイルとフォームインスタンスを渡します。
インデックスページにWebフォームを表示するには、最初にベーステンプレートを作成します。このテンプレートには、コードの繰り返しを避けるために他のテンプレートでも使用されるすべての基本的なHTMLコードが含まれます。 次に、index()
関数でレンダリングしたindex.html
テンプレートファイルを作成します。 テンプレートの詳細については、Flaskアプリケーションでテンプレートを使用する方法を参照してください。
Flaskがテンプレートを検索するflask_app
ディレクトリにtemplates
フォルダを作成し、base.html
というテンプレートファイルを開きます。これは他のテンプレートのベーステンプレートになります。
mkdir templates nano templates/base.html
base.html
ファイル内に次のコードを追加して、ナビゲーションバーとコンテンツブロックを含むベーステンプレートを作成します。
フラスコ_app/templates / base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %} {% endblock %} - FlaskApp</title> <style> nav a { color: #d64161; font-size: 3em; margin-left: 50px; text-decoration: none; } </style> </head> <body> <nav> <a href="{{ url_for('index') }}">FlaskApp</a> <a href="#">About</a> </nav> <hr> <div class="content"> {% block content %} {% endblock %} </div> </body> </html>
この基本テンプレートには、他のテンプレートで再利用する必要があるすべてのHTMLボイラープレートが含まれています。 title
ブロックは各ページのタイトルを設定するために置き換えられ、content
ブロックは各ページのコンテンツに置き換えられます。 ナビゲーションバーには2つのリンクがあります。1つはurl_for()
ヘルパー機能を使用してindex()
ビュー機能にリンクするインデックスページ用で、もう1つは[バージョン情報]ページ用です。アプリケーションで。
ファイルを保存して閉じます。
次に、index.html
というテンプレートを開きます。 これは、app.py
ファイルで参照したテンプレートです。
nano templates/index.html
このファイルには、form
変数を介してindex.html
テンプレートに渡したWebフォームが含まれます。 次のコードを追加します。
フラスコ_app/templates / index.html
{% extends 'base.html' %} {% block content %} <h1>{% block title %} Add a New Course {% endblock %}</h1> <form method="POST" action="/"> {{ form.csrf_token }} <p> {{ form.title.label }} {{ form.title(size=20) }} </p> {% if form.title.errors %} <ul class="errors"> {% for error in form.title.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} <p> {{ form.description.label }} </p> {{ form.description(rows=10, cols=50) }} {% if form.description.errors %} <ul class="errors"> {% for error in form.description.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} <p> {{ form.price.label }} {{ form.price() }} </p> {% if form.price.errors %} <ul class="errors"> {% for error in form.price.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} <p> {{ form.available() }} {{ form.available.label }} </p> {% if form.available.errors %} <ul class="errors"> {% for error in form.available.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} <p> {{ form.level.label }} {{ form.level() }} </p> {% if form.level.errors %} <ul class="errors"> {% for error in form.level.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} <p> <input type="submit" value="Add"> </p> </form> {% endblock %}
ファイルを保存して閉じます。
基本テンプレートを拡張し、<h1>
タグにタイトルを設定します。 次に、<form>
タグ内のWebフォームフィールドをレンダリングし、そのメソッドをPOST
に設定し、アクションを/
メインルート(インデックスページ)に設定します。 最初に、WTFormsがテンプレート:Form.csrf token
の行を使用して、CSRF攻撃からフォームを保護するために使用するCSRFトークンをレンダリングします。 このトークンは、残りのフォームデータとともにサーバーに送信されます。 フォームを保護するために、常にこのトークンをレンダリングすることを忘れないでください。
構文form.field()
を使用して各フィールドをレンダリングし、構文form.field.label
を使用してそのラベルをレンダリングします。 フィールドに引数を渡して、フィールドの表示方法を制御できます。 たとえば、テンプレート:Form.title(size=20)
でタイトル入力フィールドのサイズを設定し、パラメータrows
およびcols
を使用して説明テキスト領域の行数と列数を設定します。 HTMLで通常行うのと同じ方法です。 同じメソッドを使用して、class
属性などのフィールドに追加のHTML属性を渡して、CSSクラスを設定できます。
構文if form.field.errors
を使用して、検証エラーをチェックします。 フィールドにエラーがある場合は、for
ループでループし、フィールドの下のリストに表示します。
仮想環境をアクティブにしてflask_app
ディレクトリにいるときに、FLASK_APP
環境変数を使用して、アプリケーション(この場合はapp.py
)についてFlaskに通知します。 次に、FLASK_ENV
環境変数をdevelopment
に設定して、アプリケーションを開発モードで実行し、デバッガーにアクセスします。 Flaskデバッガーの詳細については、Flaskアプリケーションでエラーを処理する方法を参照してください。 これを行うには、次のコマンドを使用します(Windowsでは、export
の代わりにset
を使用します)。
export FLASK_APP=app export FLASK_ENV=development
次に、アプリケーションを実行します。
flask run
開発サーバーが実行されている状態で、ブラウザーを使用して次のURLにアクセスします。
http://127.0.0.1:5000/
インデックスページにWebフォームが表示されます。
タイトルを入力せずにフォームを送信してみてください。 タイトルが必要であることを通知するエラーメッセージが表示されます。 無効なデータ(10文字未満の短いタイトルや200文字を超える説明など)を送信してフォームを試して、他のエラーメッセージを確認してください。
フォームの送信を処理するコードがないため、これまでのところ、フォームに有効なデータを入力しても何も起こりません。 そのためのコードは後で追加します。
今のところ、リストにあるコースを表示するページが必要です。 後で、Webフォームデータを処理すると、リストに新しいコースが追加され、ユーザーをコースページにリダイレクトして、追加された新しいコースを確認します。
開発サーバーを実行したままにして、別のターミナルウィンドウを開きます。
次に、app.py
を開いて、コースルートを追加します。
nano app.py
ファイルの最後に次のルートを追加します。
フラスコ_app/app.py
# ... @app.route('/courses/') def courses(): return render_template('courses.html', courses_list=courses_list)
ファイルを保存して閉じます。
このルートは、courses.html
というテンプレートをレンダリングし、courses_list
リストを渡します。
次に、courses.html
テンプレートを作成して、コースを表示します。
nano templates/courses.html
次のコードを追加します。
フラスコ_app/templates/courses.html
{% extends 'base.html' %} {% block content %} <h1>{% block title %} Courses {% endblock %}</h1> <hr> {% for course in courses_list %} <h2> {{ course['title'] }} </h2> <h4> {{ course['description'] }} </h4> <p> {{ course['price'] }}$ </p> <p><i>({{ course['level'] }})</i></p> <p>Availability: {% if course['available'] %} Available {% else %} Not Available {% endif %}</p> <hr> {% endfor %} {% endblock %}
ファイルを保存して閉じます。
タイトルを設定し、courses_list
リストのアイテムをループします。 タイトルは<h2>
タグで、説明は<h4>
タグで、価格とコースレベルは<p>
タグで表示します。 条件if course['available']
を使用して、コースが利用可能かどうかを確認します。 コースが利用可能な場合は「利用可能」というテキストを表示し、利用できない場合は「利用不可」というテキストを表示します。
ブラウザを使用してコースページに移動します。
http://127.0.0.1:5000/courses/
これまでのコースリストにはコースが1つしかないため、1つのコースが表示されたページが表示されます。
次に、base.html
を開いて、ナビゲーションバーのコースページへのリンクを追加します。
nano templates/base.html
次のように編集します。
フラスコ_app/templates / base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %} {% endblock %} - FlaskApp</title> <style> nav a { color: #d64161; font-size: 3em; margin-left: 50px; text-decoration: none; } </style> </head> <body> <nav> <a href="{{ url_for('index') }}">FlaskApp</a> <a href="{{ url_for('courses') }}">Courses</a> <a href="#">About</a> </nav> <hr> <div class="content"> {% block content %} {% endblock %} </div> </body> </html>
ファイルを保存して閉じます。
インデックスページを更新すると、ナビゲーションバーに新しいCoursesリンクが表示されます。
アプリケーションに必要なページを作成しました。新しいコースを追加するためのWebフォームを備えたインデックスページと、リストにあるコースを表示するためのページです。
アプリケーションを機能させるには、ユーザーがWebフォームデータを送信するときに、それを検証してコースリストに追加することにより、Webフォームデータを処理する必要があります。 次にこれを行います。
ステップ4—フォームデータへのアクセス
このステップでは、ユーザーが送信したデータにアクセスして検証し、コースのリストに追加します。
app.py
を開いて、index()
関数内でWebフォームデータを処理するためのコードを追加します。
nano app.py
index()
関数を次のように編集します。
フラスコ_app/app.py
# ... @app.route('/', methods=('GET', 'POST')) def index(): form = CourseForm() if form.validate_on_submit(): courses_list.append({'title': form.title.data, 'description': form.description.data, 'price': form.price.data, 'available': form.available.data, 'level': form.level.data }) return redirect(url_for('courses')) return render_template('index.html', form=form)
ファイルを保存して閉じます。 ここでは、form
オブジェクトでvalidate_on_submit()
メソッドを呼び出します。このメソッドは、リクエストがPOSTリクエストであることを確認し、各フィールドに構成したバリデーターを実行します。 少なくとも1つのバリデーターがエラーを返した場合、条件はFalse
になり、各エラーはその原因となったフィールドの下に表示されます。
送信されたフォームデータが有効な場合、条件はTrue
であり、if
ステートメントの下のコードが実行されます。 コース辞書を作成し、append
メソッドを使用して、新しいコースをcourses_list
リストに追加します。 構文form.field.data
を使用して、各フィールドの値にアクセスします。 新しいコース辞書をコースリストに追加した後、ユーザーをCourses
ページにリダイレクトします。
開発サーバーが実行されている状態で、インデックスページにアクセスします。
http://127.0.0.1:5000/
フォームに有効なデータを入力して送信します。 Courses
ページにリダイレクトされ、新しいコースが表示されます。
結論
Flask-WTF拡張機能とWTFormsライブラリを使用して作成したWebフォームを持つFlaskアプリケーションを作成しました。 フォームには、ユーザーからデータを受け取り、特別なWTFormsバリデーターを使用してデータを検証し、データストアに追加するためのいくつかのタイプのフィールドがあります。
Flaskの詳細については、Flaskを使用してWebサイトを作成する方法シリーズの他のチュートリアルをご覧ください。