RadioField验证器无效及翻页对应问题

李老师您好!
经您提醒后,我尝试了给StringField添加Datarequired的验证器,这种情况下,当用户没有填写信息,网页不会再刷新。但是当我把表单换成RadioField并添加DataRequired的验证器之后,网页还是刷新了。我在stackoverflow上找到了这样一个回答:python - Flask-WTForms RadioField custom validator message doesn't work - Stack Overflow
似乎这是一个flask的一个bug?

另外,我按照您上次提供的方法,将session放在表单验证并提交之后,但是并没有奏效:frowning:

这次我在session中添加了[‘page_num’],希望能以此实现翻页功能。期望的效果是,如果将图片的链接(img_url),用户的选择(option)存入数据库或写入本地文档,这两个字段可以对应,并且在页数上+1,当页数到达设定的最大值时,跳转到结束页面。然而现在出现的情况是,用户的选择对应的并不是他对当前看到的图所做的选择,我将图片的url打印出来,发现图片在我提交表单的过程中出现了两次刷新。

代码如下:
demo.py

@app.route('/')
@app.route('/home',methods=['POST','GET'])
def home():
    form=myForm()
    name='Welcome to the "Online Recipe Origin" Survey'
    letters = string.ascii_letters
    user_id = ''.join(random.sample(letters, 15))
    if request.method == 'POST':
        user_id = request.form['user_id']
        session['user'] = user_id
        session['page_num'] = 0
        #print(session['user'])
        return redirect(url_for('random_image'))
    return render_template('home.html',name=name,form=form,user_id=user_id)

@app.route('/end',methods=['POST','GET'])
def end():
    name='Thanks!'
    return render_template('end.html',name=name)

@app.route('/random_image',methods=['POST','GET'])
def random_image():
    form=testform()
    names = os.listdir(os.path.join(app.static_folder, 'images'))
    img_url = url_for('static', filename=os.path.join('images', random.choice(names)))
    print(img_url)
    if form.validate_on_submit():
        while session['page_num']<5: # set the max page_num
            session['option'] = form.examples.data
            #session['img_url'] = img_url
            session['page_num'] +=1
            print(session)
            return redirect('random_image') # claer the choice the user made
        else:
            print(form.errors)
            print(session)
            return redirect('end')
    return render_template('random_image.html',img_url=img_url,form=form)

if __name__ == '__main__':
    app.run(debug=True)

form.py

class myForm(Form):
    user = StringField('user',validators=[Length(min=4,max=25),DataRequired()])
    user_id= HiddenField()
class testform(Form):
    examples = RadioField('portals', choices=[('1', 'Option1'), ('2', 'Option2'), ('3', 'Option3')],coerce=str,validators=[DataRequired()])
    BooleanField('I accept the TOS', [validators.DataRequired()])

random_image.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Recipe Survey</title>
</head>

<body>
<div class="container" style="margin-top: 50px">
<div align="center">
    <h2>Welcome to the "Online Recipe Origin" Survey</h2>
    <form method="POST" action="">
        {{ form.hidden_tag() }}

        {% for error in form.examples.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        <img src="{{ img_url }}">
        <h5>{{ img_url }}</h5>
    {{ form.examples() }}
    <button type="submit" class="'btn btn-lg btn-success">NEXT</button>
    </form>
</div>
</div>

</body>
</html>

先说第二个图片刷新两次的问题,你需要把这一行:

img_url = url_for('static', filename=os.path.join('images', random.choice(names)))

放到 if form.validate_on_submit(): 语句块的下方+外部,这样可以避免 POST 请求执行它,即:

@app.route('/random_image',methods=['POST','GET'])
def random_image():
    form=testform()
    names = os.listdir(os.path.join(app.static_folder, 'images'))
    if form.validate_on_submit():
        ....
    img_url = url_for('static', filename=os.path.join('images', random.choice(names)))
    return render_template('random_image.html',img_url=img_url,form=form)

上个帖子里给的图示有点问题,if form.validate_on_submit(): 上面的代码实际上是 GET 和 POST 请求都会执行的代码,所以 img_url 会被调用两次。更新后的图示:

RadioField 不显示错误消息是 WTForms 的 bug,至于没有添加 required 属性暂时还不清楚是谁的问题。目前来说,你可以手动渲染 examples 字段,给三个选项中的其中一个手动添加 required 属性。

这里我大概理解了。就是只要用户请求这个网页,网页就会被渲染,图片会加入到网页中。当用户提交表单发送一个post请求,页数不足的情况下,网页会重定向,然后被重新渲染。所以当网页第一次被渲染时,我从文件夹中随机抽取一张图放入网页中,然后页数不足网页重定向,网页重新渲染,这时又会从文件夹中随机抽取一张图,但是这张图有可能和前面渲染过的图相同。所以这里我不能实现图片的无放回抽取对吗。。也就是说随机从文件夹中抽取的6张图中很有可能有重复。。。

手动渲染已解决该问题。多谢老师~

要解决重复的问题可以考虑在 session 里存一个列表,包含已经抽取过的图片文件名,抽取图片的时候加个判断看列表里有没有。

嗯嗯,我也尝试过把所有文件名存进session的一个列表里,抽出一个弹出一个。现在抽取的图片已经不会重复了。多谢老师的解答:grin:

1 个赞