赵走x博客
首页
书籍
软件
工具
古诗词
搜索
登录
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 实战:35、在视图函数里操作数据库
资源编号:75886
Python Web
Flask Web开发实战:入门、进阶与原理解析
热度:33
在视图函数里操作数据库的方式和我们在Python Shell中的练习大致相同,只不过需要一些额外的工作。比如把查询结果作为参数传入模板渲染出来,或是获取表单的字段值作为提交到数据库的数据。在这一节,我们将把上一节学习的所有数据库操作知识运用到一个简单的笔记程序中。这个程序可以让你创建、编辑和删除笔记,并在主页列出所有保存后的笔记。
在视图函数里操作数据库的方式和我们在Python Shell中的练习大致相同,只不过需要一些额外的工作。比如把查询结果作为参数传入模板渲染出来,或是获取表单的字段值作为提交到数据库的数据。在这一节,我们将把上一节学习的所有数据库操作知识运用到一个简单的笔记程序中。这个程序可以让你创建、编辑和删除笔记,并在主页列出所有保存后的笔记。 # 1.Create 为了支持输入笔记内容,我们先创建一个用于填写新笔记的表单,如下所示: ``` from flask_wtf import FlaskForm from wtforms import TextAreaField, SubmitField from wtforms.validators import DataRequired class NewNoteForm(FlaskForm): body = TextAreaField('Body', validators=[DataRequired()]) submit = SubmitField('Save') ``` 我们创建一个new_note视图,这个视图负责渲染创建笔记的模板,并处理表单的提交: ``` @app.route('/new', methods=['GET', 'POST']) def new_note(): form = NewNoteForm() if form.validate_on_submit(): body = form.body.data note = Note(body=body) db.session.add(note) db.session.commit() flash('Your note is saved.') return redirect(url_for('index')) return render_template('new_note.html', form=form) ``` 我们先来看看form.validate_on_submit()返回True时的处理代码。当表单被提交且通过验证时,我们获取表单body字段的数据,然后创建新的Note实例,将表单中body字段的值作为body参数传入,最后添加到数据库会话中并提交会话。这个过程接收用户通过表单提交的数据并保存到数据库中,最后我们使用flash()函数发送提示消息并重定向到index视图。 表单在new_note.html模板中渲染,这里使用我们的form_field宏渲染表单字段,传入rows和cols参数来定制`
`输入框的大小: ``` {% block content %}
New Note
{{ form.csrf_token }} {{ form_field(form.body, rows=5, cols=50) }} {{ form.submit }}
{% endblock %} ``` index视图用来显示主页,目前它的所有作用就是渲染主页对应的模板: ``` @app.route('/') def index(): return render_template('index.html') ``` 在对应的index.html模板中,我们添加一个指向创建新笔记页面的链接: ```
Notebook
New Note
``` # 2.Read 在上一节我们为程序实现了添加新笔记的功能,当你在创建笔记的页面单击保存后,程序会重定向到主页,提示的消息告诉你刚刚提交的笔记已经成功保存了,可是你却无法看到创建后的笔记。为了在主页列出所有保存的笔记,我们需要修改index视图,修改后的index视图如代码清所示。 ``` @app.route('/') def index(): notes = Note.query.all() return render_template('index.html', notes=notes, form=form) ``` 在新的index视图里,我们像在Python Shell中一样使用Note.query.all()查询所有note记录,然后把这个包含所有记录的列表作为notes变量传入模板。你已经猜到下一步了,没错,我们将在模板中将笔记们显示出来,如代码清单所示。 ```
Notebook
New Note
{{ notes|length }} notes:
{% for note in notes %}
{{ note.body }}
{% endfor %} ``` 在模板中,我们迭代这个notes列表,调用Note对象的body属性(note.body)获取body字段的值。另外,我们还通过length过滤器获取笔记的数量。渲染后的示例如图5-1所示。 ![image.png](https://img.hacpai.com/file/2019/09/image-a9811b49.png) 图5-1 显示笔记列表 # 3.Update 更新一条笔记和创建一条新笔记的实现代码几乎完全相同,首先是编辑笔记的表单: ``` class EditNoteForm(FlaskForm): body = TextAreaField('Body', validators=[DataRequired()]) submit = SubmitField('Update') ``` 你会发现这和创建新笔记NewNoteForm唯一的不同就是提交字段的标签参数(作为
的value属性),因此这个表单的定义也可以通过继承来简化: ``` class EditNoteForm(NewNoteForm): submit = SubmitField('Update') ``` 用来渲染更新笔记页面和处理更新表单提交的edit_note视图如代码清单所示。 ``` @app.route('/edit/
', methods=['GET', 'POST']) def edit_note(note_id): form = EditNoteForm() note = Note.query.get(note_id) if form.validate_on_submit(): note.body = form.body.data db.session.commit() flash('Your note is updated.') return redirect(url_for('index')) form.body.data = note.body return render_template('edit_note.html', form=form) ``` 这个视图通过URL变量note_id获取要被修改的笔记的主键值(id字段),然后我们就可以使用get()方法获取对应的Note实例。当表单被提交且通过验证时,我们将表单中body字段的值赋给note对象的body属性,然后提交数据库会话,这样就完成了更新操作。和创建笔记相同,我们接着发送提示消息并重定向到index视图。 唯一需要注意的是,在GET请求的执行流程中,我们添加了下面这行代码: ``` form.body.data = note.body ``` 因为要添加修改笔记内容的功能,那么当我们打开修改某个笔记的页面时,这个页面的表单中必然要包含笔记原有的内容。 如果手动创建HTML表单,那么你可以通过将note记录传入模板,然后手动为对应字段中填入笔记的原有内容,比如: ```
{{ note.body }}
``` 其他input元素则通过value属性来设置输入框中的值,比如: ```
``` 使用WTForms可以省略这些步骤,当我们渲染表单字段时,如果表单字段的data属性不为空,WTForms会自动把data属性的值添加到表单字段的value属性中,作为表单的值填充进去,我们不用手动为value属性赋值。因此,将存储笔记原有内容的note.body属性赋值给表单body字段的data属性即可在页面上的表单中填入原有的内容。 模板的内容基本相同,这里不再赘述。最后的工作是在主页笔记列表中的每个笔记内容下添加一个编辑按钮,用来访问编辑页面: ``` {% for note in notes %}
{{ note.body }}
Edit
{% endfor %} ``` 生成edit_note视图的URL时,我们传入当前note对象的id(note.id)作为URL变量note_id的值。 # 4.Delete 在程序中,删除的实现也非常简单,不过这里经常会有一个误区。大多数人通常会考虑在笔记内容下添加一个删除链接: ```
Delete
``` 这个链接指向用来删除笔记的delete_note视图: ``` @app.route('/delete/
') def delete_note(note_id): note = Note.query.get(note_id) db.session.delete(note) db.session.commit() flash('Your note is deleted.') return redirect(url_for('index')) ``` 虽然这一切看起来都很合理,但这种处理方式实际上会使程序处于CSRF攻击的风险之中。我们在第2章曾强调过,防范CSRF攻击的基本原则就是正确使用GET和POST方法。像删除这类修改数据的操作绝对不能通过GET请求实现,正确的做法是为删除操作创建一个表单,如下所示: ``` class DeleteNoteForm(FlaskForm): submit = SubmitField('Delete') ``` 这个表单类只有一个提交字段,因为我们只需要在页面上显示一个删除按钮来提交表单。删除表单的提交请求由delete_note视图处理,如代码清单所示。 ``` @app.route('/delete/
', methods=['POST']) def delete_note(note_id): form = DeleteForm() if form.validate_on_submit(): note = Note.query.get(note_id) # 获取对应记录 db.session.delete(note) # 删除记录 db.session.commit() # 提交修改 flash('Your note is deleted.') else: abort(400) return redirect(url_for('index')) ``` 在delete_note视图的app.route()中,methods列表仅填入了POST,这会确保该视图仅监听POST请求。 和编辑笔记的视图类似,这个视图接收note_id(主键值)作为参数。如果提交表单且通过验证(唯一需要被验证的是CSRF令牌),就使用get()方法查询对应的记录,然后调用db.session.delete()方法删除并提交数据库会话。如果验证出错则使用abort()函数返回400错误响应。 因为删除按钮要在主页的笔记内容下添加,我们需要在index视图中实例化DeleteNote-Form类,然后传入模板。在index.html模板中,我们渲染这个表单: ``` {% for note in notes %}
{{ note.body }}
Edit
{{ form.csrf_token }} {{ form.submit(class='btn') }}
{% endfor %} ``` 我们将表单的action属性设置为删除当前笔记的URL。构建URL时,URL变量note_id的值通过note.id属性获取,当单击提交按钮时,会将请求发送到action属性中的URL。添加删除表单的主要目的就是防止CSRF攻击,所以不要忘记渲染CSRF令牌字段form.csrf_token。 在HTML中,
标签会显示为链接,而提交按钮会显示为按钮,为了让编辑和删除笔记的按钮显示相同的样式,我们为这两个元素使用了同一个CSS类“.btn”,具体可以在static/style.css文件中查看。作为替代,你可以考虑使用JavaScript创建监听函数,当删除按钮按下时,提交对应的隐藏表单。