Flask-WTFでWebフォームを使用および検証する方法

提供:Dev Guides
移動先:案内検索

著者は、 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リストに保存されているコースを表示するためのページがあり、インデックスページには、コースのタイトル、説明、価格、可用性、およびレベル(初級、中級、または上級)を入力するためのフォームがあります。

前提条件

ステップ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つの引数を取ります。 min10に設定され、タイトルの長さが10文字以上になるようにします。また、max100に設定され、そうでないことを確認します。 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からインポートし、CourseFormforms.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パラメーターでGETPOSTHTTPメソッドの両方を受け入れます。 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サイトを作成する方法シリーズの他のチュートリアルをご覧ください。