質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1841閲覧

FlaskFormでの読み込みエラー

EiAma

総合スコア15

Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/01/08 16:08

編集2021/10/30 02:15

前提・実現したいこと

Flaskを用いて評価フォームを準備し、入力されたデータをデータベースにあげるという一連の関数を作成中です。
同じ型のFlaskFormをfor文で作成し、リストとしてまとめた物を別のリストとzip関数で辞書型にまとめ、render_template時に渡し、HTML上のテンプレートタグにて展開することができました。

しかし、submitされた際に入力された内容をそれぞれデータベース上に上げようとすると、データがすべてリストにおける一つ目のFlaskFormで入力されたものになってしまいます。

評価項目数に応じてHTMLに渡すFlaskFormの数を変えたいためfor文にて作成しています。

submitフォームは別のFlaskFormにて定義しています。

  1. FlaskFormの定義
  2. render_template時にFormを渡す
  3. validate関数にエラーがない場合はデータベースにあげる

"3."のデータベースにデータをあげることはできますが、データが入力されたものと一致していない。

テストコード

ファイル構造
.
├── app.py
└── templates
---├── evaluate.html
---└── evaluated.html

app.py

python

1from flask import Flask, render_template, url_for, redirect 2from wtforms.validators import DataRequired 3from flask_wtf import FlaskForm 4from wtforms import StringField, PasswordField, SubmitField, BooleanField, SelectField, TextAreaField, IntegerField 5 6app = Flask(__name__) 7app.config['SECRET_KEY'] = '1234567890' 8 9 10class Submit(FlaskForm): 11 year = IntegerField('評価年', validators=[DataRequired()], default = 2020) 12 submit = SubmitField('登録') 13 14class Evaluation(FlaskForm): 15 value = StringField('評価値', validators=[DataRequired()]) 16 message = TextAreaField('コメント', render_kw={'rows': 4}) 17 18@app.route('/', methods=['GET','POST']) 19def home(): 20 21 form = Submit() 22 23 list_of_form = [] 24 list_of_function_name = ['Column1', 'Column2', 'Column3'] 25 26 for a in range(len(list_of_function_name)): 27 list_of_form.append(Evaluation()) 28 print(list_of_form) 29 30 list_of_form_dictionary = dict(zip(list_of_function_name, list_of_form)) 31 32 if form.validate_on_submit(): 33 for a in range(len(list_of_form)): 34 print(print(list_of_form[a].value.data)) 35 print(print(list_of_form[a].message.data)) 36 37 return render_template('evaluated.html', title = '評価値', list_of_form = list_of_form ) 38 39 return render_template('evaluate.html', title = 'テスト', form = form, evaluation_list = list_of_form_dictionary) 40

evaluate.html

HTML

1<!DOCTYPE html> 2<html> 3<head> 4 <!-- Required meta tags --> 5 <meta charset="utf-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> 7 8 <!-- Bootstrap CSS --> 9 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" crossorigin="anonymous"> 10 11 {% if title %} 12 <title>{{ title }}</title> 13 {% else %} 14 <title>HOME</title> 15 {% endif %} 16</head> 17<body> 18 <header class="site-header"> 19 </header> 20 <main role="main" class="container"> 21 <div class="row"> 22 <div class="col-md-12"> 23 <div class="container"> 24 <form method="POST" action=""> 25 {{ form.hidden_tag() }} 26 <fieldset class="form-group"> 27 <legend class="border-bottom mb-4">評価</legend> 28 <div class="form-group"> 29 {{ form.year.label(class="form-control-label") }} 30 {% if form.year.errors %} 31 {{ form.year(class="form-control form-control-lg is-invalid") }} 32 <div class="invalid-feedback"> 33 {% for error in form.year.errors %} 34 <span>エラー</span> 35 {% endfor %} 36 </div> 37 {% else %} 38 {{ form.year(class="form-control form-control-lg") }} 39 {% endif %} 40 </div> 41 42 {% for name, evaluation_form in evaluation_list.items() %} 43 <h3>{{ name[5:] }}</h3> 44 <div class="form-group"> 45 {{ evaluation_form }} 46 {{ evaluation_form.value.label(class="form-control-label") }} 47 {% if evaluation_form.value.errors %} 48 {{ evaluation_form.value(class="form-control form-control-lg is-invalid") }} 49 <div class="invalid-feedback"> 50 {% for error in evaluation_form.value.errors %} 51 <span>エラー</span> 52 {% endfor %} 53 </div> 54 {% else %} 55 {{ evaluation_form.value(class="form-control form-control-lg") }} 56 {% endif %} 57 </div> 58 <div class="form-group"> 59 {{ evaluation_form.message.label(class="form-control-label") }} 60 {% if evaluation_form.message.errors %} 61 {{ evaluation_form.message(class="form-control form-control-lg is-invalid") }} 62 <div class="invalid-feedback"> 63 {% for error in evaluation_form.message.errors %} 64 <span>エラー</span> 65 {% endfor %} 66 </div> 67 {% else %} 68 {{ evaluation_form.message(class="form-control form-control-lg") }} 69 {% endif %} 70 </div> 71 {% endfor %} 72 73 74 </fieldset> 75 76 <div class="form-group"> 77 {{ form.submit(class="btn btn-outline-info") }} 78 </div> 79 </form> 80 </div> 81 </div> 82 </div> 83 </main> 84</body> 85</html> 86

evaluated.html

HTML

1<!DOCTYPE html> 2<html> 3<head> 4 <!-- Required meta tags --> 5 <meta charset="utf-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> 7 8 <!-- Bootstrap CSS --> 9 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" crossorigin="anonymous"> 10 11 {% if title %} 12 <title>{{ title }}</title> 13 {% else %} 14 <title>HOME</title> 15 {% endif %} 16</head> 17<body> 18 <header class="site-header"> 19 </header> 20 <main role="main" class="container"> 21 <div class="row"> 22 <div class="col-md-12"> 23 <div class="container"> 24 {{ list_of_form }} 25 {% for written in list_of_form %} 26 <h3>{{ written.value.data }}</h3> 27 <h4>{{ written.message.data }}</h4> 28 <br> 29 {% endfor %} 30 </div> 31 </div> 32 </div> 33 </main> 34</body> 35</html> 36

試したこと

for文によって作成したFlaskFormの格納アドレスとHTML側に渡された後での各FlaskFormの格納アドレスが一致しているか。
結果、同じアドレスに保存されているFormを用いている。

HTMLコード4行目
{{ evaluation_form }}
にて試験的に出力された値と比較

補足情報(FW/ツールのバージョンなど)

Flask==1.1.1
Flask-WTF==0.14.2
WTForms==2.2.1
Python 3.7.4

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

FiroProchainezo

2020/01/09 02:33

そのまま貼り付けたらテスト実行できるソースを提示いただきたいのですが可能ですか? 実行してみようとしたのですが、足りないものだらけで挫折しました。
EiAma

2020/01/09 05:21

情報不足が多く大変失礼いたしました。 上部のメインの部分に実行可能なコードを付け加えさせていただきます。 実行できませんようでしたらご連絡いただけたらと思います。 よろしくお願いいたします。
guest

回答1

0

ベストアンサー

実行してみたところ、htmlが以下になっていました。(form部分のみ抜粋)

html

1<form method="POST" action=""> 2 <input id="csrf_token" name="csrf_token" type="hidden" value="ImVjNzRmN2NjNjYzMjQ4YTFkNzI5N2U2YWNjZGIyY2JmYjU1NTJhYTAi.XhbENg.voQOBUIilCBVefGLbMGAxddeBhc"> 3 <fieldset class="form-group"> 4 <legend class="border-bottom mb-4">評価</legend> 5 <div class="form-group"> 6 <label class="form-control-label" for="year">評価年</label> 7 8 <input class="form-control form-control-lg" id="year" name="year" required type="text" value="2020"> 9 10 </div> 11 12 13 <h3>n1</h3> 14 <div class="form-group"> 15 &lt;app.Evaluation object at 0x0000024BC9E93B48&gt; 16 <label class="form-control-label" for="value">評価値</label> 17 18 <input class="form-control form-control-lg" id="value" name="value" required type="text" value=""> 19 20 </div> 21 <div class="form-group"> 22 <label class="form-control-label" for="message">コメント</label> 23 24 <textarea class="form-control form-control-lg" id="message" name="message" rows="4"></textarea> 25 26 </div> 27 28 <h3>n2</h3> 29 <div class="form-group"> 30 &lt;app.Evaluation object at 0x0000024BC9E93BC8&gt; 31 <label class="form-control-label" for="value">評価値</label> 32 33 <input class="form-control form-control-lg" id="value" name="value" required type="text" value=""> 34 35 </div> 36 <div class="form-group"> 37 <label class="form-control-label" for="message">コメント</label> 38 39 <textarea class="form-control form-control-lg" id="message" name="message" rows="4"></textarea> 40 41 </div> 42 43 <h3>n3</h3> 44 <div class="form-group"> 45 &lt;app.Evaluation object at 0x0000024BC9E93D08&gt; 46 <label class="form-control-label" for="value">評価値</label> 47 48 <input class="form-control form-control-lg" id="value" name="value" required type="text" value=""> 49 50 </div> 51 <div class="form-group"> 52 <label class="form-control-label" for="message">コメント</label> 53 54 <textarea class="form-control form-control-lg" id="message" name="message" rows="4"></textarea> 55 56 </div> 57 58 59 60 </fieldset> 61 62 <div class="form-group"> 63 <input class="btn btn-outline-info" id="submit" name="submit" type="submit" value="登録"> 64 </div> 65</form>

見ていただくとわかると思いますが、評価値はvalue、コメントはmessageというidになっています。
(手元の環境で実行したところ、一番上の値だけ生きていました)

idはユニークでなければならないため、同じidのフィールドがあるのがNGです。
idをユニークにするには、別の名前で定義する必要があると思いますので、以下の様に定義して使う必要があるはずです。

python

1class Evaluation(FlaskForm): 2 year = IntegerField('評価年', validators=[DataRequired()], default=2020) 3 value01 = StringField('評価値', validators=[DataRequired()]) 4 message01 = TextAreaField('コメント', render_kw={'rows': 4}) 5 value02 = StringField('評価値', validators=[DataRequired()]) 6 message02 = TextAreaField('コメント', render_kw={'rows': 4}) 7 value03 = StringField('評価値', validators=[DataRequired()]) 8 message03 = TextAreaField('コメント', render_kw={'rows': 4}) 9 submit = SubmitField('登録')

送られてきている値は、app.pyにでfrom flask import requestを追加し、以下を実行すると閲覧できるはずです。

python

1# app.pyのファイル先頭に追加 2from flask import request 3 4# app.pyのhome()に追加 5 for n in request.form: 6 print('[{key}] = {value}'.format(key=n, value=request.form[n]))

面倒だったら以下でも閲覧可能です。

python

1# app.pyのファイル先頭に追加 2from flask import request 3import pprint 4 5# app.pyのhome()に追加 6pprint.pprint(request.form)

ちょこっと調べた感じでは、同じform classを使い回す方法がなかったので、個別に作成するしかないように思います。
それでもなお可変にしたい場合は、flask-formを使うのを諦めて、formの定義を自分でするしか無いかもしれません。

投稿2020/01/09 06:34

FiroProchainezo

総合スコア2424

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

EiAma

2020/01/09 09:51 編集

回答ありがとうございます。 FiroProchainezoさんがアドバイスくださったidタグ関連についてのご指摘から、関連を調べたところ、name属性が一致してしまっていることも問題のようでした。 したがって、Form作成時に合わせてidとnameの値を変える方法を探したところ、 submit = SubmitField('Create', id='submit_button', _name='submit_button') #2 このようなコードを書いておられる方がおり、python上で動的に各Formのidとnameの値を変えることができると見込みました。 for a in range(len(info_of_function)): list_of_form.append(Evaluation()) list_of_form[a].value.id = "value"+str(a) list_of_form[a].value.name = "value"+str(a) list_of_form[a].message.id = "message"+str(a) list_of_form[a].message.name = "message"+str(a) このような記述で各idとnameの値を指定することができ、request.form[name]を用いてそれぞれ抜き出すことができました。 少々面倒くさいところですが、この方法にてFlaskFormの使い回しができるかと思われます。 しかし、validate()が効かない見たいで、別途記述する必要がありそうです。 ご指摘いただいた点から導けたことですので、ベストアンサーとさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問