赵走x博客
网站访问量:151874
首页
书籍
软件
工具
古诗词
搜索
登录
Flask 实战:41、电子邮件进阶实践
Flask 实战:40、使用事务邮件服务SendGird
Flask 实战:39、使用Flask-Mail发送电子邮件
Flask 实战:38、数据库进阶实践
Flask 实战:37、更新数据库表
Flask 实战:36、定义关系
Flask 实战:35、在视图函数里操作数据库
Flask 实战:34、数据库操作:CRUD
Flask 实战:33、使用Flask-SQLAlchemy管理数据库
Flask 实战:32、ORM魔法
Flask 实战:31、数据库的分类
Flask 实战:30、单个页面多个表单
Flask 实战:29、单个表单多个提交按钮
Flask 实战:28、使用Flask-CKEditor集成富文本编辑器
Flask 实战:27、多文件上传
Flask 实战:26、文件上传
Flask 实战:25、自定义验证器
Flask 实战:24、使用宏渲染表单
Flask 实战:23、设置错误消息语言
Flask 实战:22、处理表单数据
Flask 实战:21、使用Flask-WTF处理表单
Flask 实战:20、HTML表单
Flask 实战:19、模板进阶实践
Flask 实战:18、模板结构组织
Flask 实战:17、模板辅助工具
Flask 实战:16、模板基本用法
Flask 实战:15、HTTP进阶实践
Flask 实战:14、Flask上下文
Flask 实战:13、HTTP响应
Flask 实战:12、HTTP请求
Flask 实战:11、请求响应循环
Flask 实战:10、Flask与MVC架构
Flask 实战:9、模板与静态文件
Flask 实战:8、Flask命令
Flask 实战:7、URL与端点
Flask 实战:6、项目配置
Flask 实战:5、Flask扩展
Flask 实战:4、Python Shell
Flask 实战:3、启动开发服务器
Flask 实战:2、Hello,Flask!
Flask 实战:1、初识Flask
Flask 实战:30、单个页面多个表单
资源编号:75881
Python Web
Flask Web开发实战:入门、进阶与原理解析
热度:76
除了在单个表单上实现多个提交按钮,有时我们还需要在单个页面上创建多个表单。比如,在程序的主页上同时添加登录和注册表单。当在同一个页面上添加多个表单时,我们要解决的一个问题就是在视图函数中判断当前被提交的是哪个表单。
除了在单个表单上实现多个提交按钮,有时我们还需要在单个页面上创建多个表单。比如,在程序的主页上同时添加登录和注册表单。当在同一个页面上添加多个表单时,我们要解决的一个问题就是在视图函数中判断当前被提交的是哪个表单。 # 1.单视图处理 创建两个表单,并在模板中分别渲染并不是难事,但是当提交某个表单时,我们就会遇到问题。Flask-WTF根据请求方法判断表单是否提交,但并不判断是哪个表单被提交,所以我们需要手动判断。基于上一节介绍的内容,我们知道被单击的提交字段最终的data属性值是布尔值,即True或False。而解析后的表单数据使用input字段的name属性值作为键匹配字段数据,也就是说,如果两个表单的提交字段名称都是submit,那么我们也无法判断是哪个表单的提交字段被单击。 解决问题的第一步就是为两个表单的提交字段设置不同的名称: ``` class SigninForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), Length(1, 20)]) password = PasswordField('Password', validators=[DataRequired(), Length(8, 128)]) submit1 = SubmitField('Sign in') class RegisterForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), Length(1, 20)]) email = StringField('Email', validators=[DataRequired(), Email(), Length(1, 254)]) password = PasswordField('Password', validators=[DataRequired(), Length(8, 128)]) submit2 = SubmitField('Register') ``` 在视图函数中,我们分别实例化这两个表单,根据提交字段的值来区分被提交的表单: ``` @app.route('/multi-form', methods=['GET', 'POST']) def multi_form(): signin_form = SigninForm() register_form = RegisterForm() if signin_form.submit1.data and signin_form.validate(): username = signin_form.username.data flash('%s, you just submit the Signin Form.' % username) return redirect(url_for('index')) if register_form.submit2.data and register_form.validate(): username = register_form.username.data flash('%s, you just submit the Register Form.' % username) return redirect(url_for('index')) return render_template('2form.html', signin_form=signin_form, register_form=register_form) ``` 在视图函数中,我们为两个表单添加了各自的if判断,在这两个if语句的内部,我们分别执行各自的代码逻辑。以登录表单(SigninForm)的if判断为例,如果signin_form.submit1.data的值为True,那就说明用户提交了登录表单,这时我们手动调用signin_form.validate()对这个表单进行验证。 这两个表单类实例通过不同的变量名称传入模板,以便在模板中相应渲染对应的表单字段,如下所示: ``` ...
{{ signin_form.csrf_token }} {{ form_field(signin_form.username) }} {{ form_field(signin_form.password) }} {{ signin_form.submit1 }}
Register Form
{{ register_form.csrf_token }} {{ form_field(register_form.username) }} {{ form_field(register_form.email) }} {{ form_field(register_form.password) }} {{ register_form.submit2 }}
... ``` 访问[http://localhost:5000/multi-form](http://localhost:5000/multi-form) 打开示例页面,当提交某个表单后,你会在重定向后的页面的提示消息里看到提交表单的名称。 # 2.多视图处理 除了通过提交按钮判断,更简洁的方法是通过分离表单的渲染和验证实现。这时表单的提交字段可以使用同一个名称,在视图函数中处理表单时也只需使用我们熟悉的form.validate_on_submit()方法。 在介绍表单处理时,我们在同一个视图函数内处理两类工作:渲染包含表单的模板(GET请求)、处理表单请求(POST请求)。如果你想解耦这部分功能,那么也可以分离成两个视图函数处理。当处理多个表单时,我们可以把表单的渲染在单独的视图函数中处理,如下所示: ``` @app.route('/multi-form-multi-view') def multi_form_multi_view(): signin_form = SigninForm2() register_form = RegisterForm2() return render_template('2form2view.html', signin_form=signin_form, register_form=register_form) ``` 这个视图只负责处理GET请求,实例化两个表单类并渲染模板。另外我们再为每一个表单单独创建一个视图函数来处理验证工作。处理表单提交请求的视图仅监听POST请求,如代码清单4-24所示。 代码清单4-24 form/app.py:使用单独的视图函数处理表单提交的POST请求 ``` @app.route('/handle-signin', methods=['POST']) # 仅传入POST到methods中 def handle_signin(): signin_form = SigninForm2() register_form = RegisterForm2() if signin_form.validate_on_submit(): username = signin_form.username.data flash('%s, you just submit the Signin Form.' % username) return redirect(url_for('index')) return render_template('2form2view.html', signin_form=signin_form, register_form=register_form) @app.route('/handle-register', methods=['POST']) def handle_register(): signin_form = SigninForm2() register_form = RegisterForm2() if register_form.validate_on_submit(): username = register_form.username.data flash('%s, you just submit the Register Form.' % username) return redirect(url_for('index')) return render_template('2form2view.html', signin_form=signin_form, register_form=register_form) ``` 在HTML中,表单提交请求的目标URL通过action属性设置。为了让表单提交时将请求发送到对应的URL,我们需要设置action属性,如下所示: ``` ...
Login Form
...
Register Form
...
... ``` 虽然现在可以正常工作,但是这种方法有一个显著的缺点。如果验证未通过,你需要将错误消息的form.errors字典传入模板中。在处理表单的视图中传入表单错误信息,就意味着需要再次渲染模板,但是如果视图函数中还涉及大量要传入模板的变量操作,那么这种方式会带来大量的重复。 对于这个问题,一般的解决方式是通过其他方式传递错误消息,然后统一重定向到渲染表单页面的视图。比如,使用flash()函数迭代form.errors字典发送错误消息(这个字典包含字段名称与错误消息列表的映射),然后重定向到用来渲染表单的multi_form_multi_view视图。下面是一个使用flash()函数来发送表单错误消息的便利函数: ``` def flash_errors(form): for field, errors in form.errors.items(): for error in errors: flash(u"Error in the %s field - %s" % ( getattr(form, field).label.text, error )) ``` 如果你希望像往常一样在表单字段下渲染错误消息,可以直接将错误消息字典form.errors存储到session中,然后重定向到用来渲染表单的multi_form_multi_view视图。在模板中渲染表单字段错误时添加一个额外的判断,从session中获取并迭代错误消息。