我是 Flask 的新手,我正在嘗試做一些需要一些技能的東西,我在搜索 SO 時找不到答案。
所以,這個概念是......我有一個動態生成的歌曲串列。基本上我們不知道會有多少。歌曲存盤在串列中的串列中,如下所示:
[[id,artist_name,track_name],[id,artist_name,track_name],[id,artist_name,track_name] etc]
我想為串列中的每個串列創建一個帶有 SelectField 的表單,以便用戶可以為串列中的每首歌曲評分。
通過串列項的路由如下所示:
@app.route('/submitlist', methods=['GET', 'POST'])
def submitlist():
form = forms.Playlist()
if request.method == 'POST':
if form.validate():
song_data = [[id,artist_name,track_name],[id,artist_name,track_name]...]
session['thesongs'] = song_data
return redirect(url_for('songs'))
return render_template('songs.html', form=form)
接收串列的路由如下所示:
@app.route('/songs', methods=['GET', 'POST'])
def songs():
form = forms.SongsRated()
if request.method == 'POST':
data = form.rating.data
session['results'] = data
return redirect(url_for('results'))
return render_template('songs.html', thesongs=session['thesongs'], form=form)
我無法弄清楚的是 SongsRated 的形式應該是什么樣子,應該回傳動態數量的 SelectFields。我還應該能夠收集回傳值并確定哪個 SelectField 值屬于串列中的哪個專案(歌曲)。
最后我想做一些驗證,因為我只希望用戶能夠為 10 首歌曲評分(不管有多少),并且評分都應該是唯一的(1-10)。
如果沒有很好地解釋,我很抱歉。
提前致謝。
uj5u.com熱心網友回復:
您可以在本教程中找到有關如何在視圖中動態創建表單的說明。
基于此,我為您撰寫了以下示例。
為串列中的每個專案創建一個帶有選擇欄位的表單。如果提交了表單,它將為您提供所有分配排名的條目的 ID 和選定值。標識基于欄位名稱,因為它包含條目的原始 ID。自定義驗證器
可以防止雙重選擇。
此外,JavaScript 完全防止雙重選擇的發生。為此,每個 SelectField 都添加了一個更改事件的偵聽器,這會禁用所有其他選擇的排名,或者如果選擇了不同的排名,則會再次啟用它。
燒瓶(app.py)
from flask import (
Flask,
render_template,
request
)
from flask_wtf import FlaskForm
from wtforms import SelectField
from wtforms.validators import ValidationError
app = Flask(__name__)
app.secret_key = 'your secret here'
# Check if a ranking was selected twice.
def validate_rating(form, field):
if field.data:
for _f in form:
if _f != field and _f.data == field.data:
raise ValidationError('A rating can only be given once.')
class SongsRated(FlaskForm):
pass
songs = [(i, f'Unknown Artist {i}', f'Untitled Track {i}') for i in range(1,16)]
@app.route('/', methods=['GET', 'POST'])
def index():
# Generate the actual form.
F = SongsRated
for id,artist,track in songs:
field = SelectField(
f'{artist} - {track}',
[validate_rating],
choices=[(i if i > 0 else '',i) for i in range(11)],
validate_choice=False
)
setattr(F, f'track-{id}', field)
# Create an instance of the form.
form = F(request.form)
# Once the form has been received and the entry is valid, ...
if form.validate_on_submit():
# ... inquire about the rankings awarded.
id_values = [
(int(f.name[6:]), int(f.data)) for f in form \
if f.name.startswith('track') and f.data
]
return render_template('index.html', **locals())
HTML (模板/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Index</title>
</head>
<body>
<form method="post">
{{ form.csrf_token }}
{% for field in form -%}
{% if field.name.startswith('track') -%}
<div>
{{ field.label() }}
{{ field() }}
{% if field.errors -%}
<ul>
{% for error in field.errors -%}
<li>{{ error }}</li>
{% endfor -%}
</ul>
{% endif -%}
</div>
{% endif -%}
{% endfor -%}
<input type="submit" />
</form>
{% if id_values -%}
<output>{{ id_values }}</output>
{% endif -%}
<script type="text/javascript">
/* This script is optional and not strictly required. */
(() => {
const elems = document.querySelectorAll('select[name^="track-"]');
const temp = Array.from(elems, elem => elem.value);
elems.forEach(elem => {
// Initialize the previous selection.
elem.value && (elem.dataset.prev = elem.value);
Array.from(elem.options).forEach(opt => {
opt.disabled = opt.value
&& opt.value != elem.value
&& temp.includes(opt.value);
});
// Register event listeners.
elem.addEventListener('change', evt => {
// Enable and disable based on the selection made.
const val = evt.target.value
const prev = evt.target.dataset.prev;
elems.forEach(sel => {
if (sel != evt.target) {
Array.from(sel.options).forEach(opt => {
if (opt.value == val && val != '') {
opt.disabled = true;
} else if (opt.value == prev) {
opt.disabled = false;
}
});
}
});
evt.target.dataset.prev = val;
});
});
})()
</script>
</body>
</html>
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/435982.html
