「Flask本格入門」でメモの更新が出来ない不具合について―バリデーションのforms.pyで作成・編集の場合分けが必要

株式会社フルネスから2023年9月に刊行された「Flask 本格入門-やさしくわかるWebアプリ開発」〔樹下雅章著〕は、説明がとても丁寧で分かり易く応用の利く好著ですが、サイト管理者(筆者)の場合、題材のメモアプリのメモが編集できませんでした。メモのタイトルが同じ場合は、新規登録も更新もできないように独自バリデーションが追記されているためです。この独自バリデーションの前後で新規登録か編集かのいずれかであることの場合分けが必要でした。

編集画面でコンテンツ(内容)だけ修正して登録しようとすると次のようになります。

これは、バリデーションを記したforms.pyで次のように独自バリデーションが追記されているためです。

# カスタムバリデータ
def validate_title(self, title):
        # StringFieldオブジェクトではなく、その中のデータ(文字列)をクエリに渡す必要があるため
        # 以下のようにtitle.dataを使用して、StringFieldから実際の文字列データを取得する
        memo = Memo.query.filter_by(title=title.data).first()
        if memo:
            raise ValidationError(f“タイトル ‘{title.data}‘ は既に存在します。\
                                  別のタイトルを入力してください。”)

この問題を回避するために、モジュール間で共通に使える変数を確保する必要がありますが、いろいろ調べてみましたら、Pkythonの言語説明サイトに記してありました(https://docs.python.org/ja/3/faq/programming.html#how-do-i-share-global-variables-across-modules)。

一つのプログラムのモジュール間で情報を共有する正準な方法は、特別なモジュール (しばしば config や cfg と呼ばれる) を作ることです。単に設定モジュールをアプリケーションのすべてのモジュールにインポートしてください。このモジュールはグローバルな名前として使えます。それぞれのモジュールのただ一つのインスタンスがあるので、設定モジュールオブジェクトに対するいかなる変更も全体に反映されます。例えば:

config.py:

x = 0   # Default value of the 'x' configuration setting

mod.py:

import config
config.x = 1

main.py:

import config
import mod
print(config.x)

なお、同じ理由から、モジュールを使うということは、シングルトンデザインパターンを実装することの基礎でもあります。

サイト管理者(筆者)の場合は、ルートのディレクトリにcommon.pyというファイルを作り、次のように書いたうえで、memo/views.pyとforms.pyを少し修正しました。

まず、commons.pyです。

is_update = False

次に、memo/views.pyです。

@memo_bp.route("/update/", methods=['GET', 'POST'])
@login_required
def update(memo_id):
    # アップデートであることを示す
    common.is_update = True
    # データベースからmemo_idに一致するメモを取得し、見つからない場合は404エラーを表示
    target_data = Memo.query.filter_by(id=memo_id, user_id=current_user.id).first_or_404()
    form = MemoForm(obj=target_data)
     
    if request.method == 'POST' and form.validate():
       target_data.title = form.title.data
       target_data.content = form.content.data
       db.session.merge(target_data)
       db.session.commit()
       flash("変更しました。")
       return redirect(url_for("memo.index"))
    # GET
    return render_template("memo/update_form.html", form=form, edit_id=target_data.id)

モジュール間の共通変数としてのis_updateをTrueとして、forms.pyに知らせるようにします。

# メモ用入力クラス
class MemoForm(FlaskForm):
    title = StringField('タイトル', validators=[DataRequired('タイトルは必須入力です'),
    Length(max=20, message='20文字以下で入力して下さい')])
    content = TextAreaField('内容')
    submit = SubmitField('送信')def validate_title(self, title):
    memo = Memo.query.filter_by(title=title.data).first()
    if memo:
        if common.is_update == False:
            raise ValidationError(f"タイトル'(title.data)は既に存在します。")
        else:
            common.is_update = True

これで、編集時(修正時)には独自バリデーションが効かなくなります。なお、各モジュールではcommon.pyモジュールのインポートが必要です。

import common

ちょっとした修正ですが、その結果次の画像のように編集できるようになりました。

本件を除いて「Flask本格入門」にソース・コードに問題はありませんでした。好著でありましたが、Flaskがマイクロフレームワークとして優れていることをあますことなく紹介しています。

【追記】第17章のマイクロ・サービスのところで、hello-appディレクトリ配下のapp.py、login.htmlともに「shwo_login」となっていますが、「show_login」でも動作します。

この記事が気に入ったら
フォローしよう

最新情報をお届けします

Twitterでフォローしよう