实验2、Flask模板、表单、视图和重定向示例
实验内容
1. 实验内容
表单功能与页面跳转功
能是Web应用程序的基础功能,学习并使用他们能够更好的完善应用程序的功能。Flask使用了名为Jinja2的模板引擎,该引擎根据用户的交互级别显示应用程序的行为。Jinja模板使用变量,表达式和标签。在浏览器中呈现页面之前,运行时期间将变量和表达式替换为值。Jinja标签有助于编写逻辑,并控制Flask模板中的语句。
2. 实验要点
- 掌握Flask模板是使用规则
- 学习并掌握Flask表单的用法
- 学习并掌握Flask页面跳转的实现
- 尝试对代码进行调试
3.实验环境
- Centos 7.9
4. 工作目录
本实验的工作目录为: /experiment
Flask 视图
Flask视图的概念来源于一种流行的web应用程序设计模式,称为模型-视图-控制器(Model-View-Controller)。视图是该范式中三个相互关联的元素之一,处理应用程序逻辑。视图负责向用户表示信息。
在上一篇教程中,我们通过继承Flask-Appbuilder
的BaseView
类来设计一个视图。在本教程的后续部分,我们将扩展我们的最后一个例子,并展示视图可以定制的方法。
Flask 模板
启动MongoDB服务
mongod --dbpath /var/lib/mongodb --logpath /var/log/mongodb/mongod.log --fork
在template
目录下创建hello.html
文件
写入下面内容并保存:
<!doctype html>
<html>
<h1>LiuYang</h1>
<p> Hello World!</p>
</html>
之后修改view.py
中的HelloWorld
类:
from flask import render_template
from flask_appbuilder import ModelView, BaseView, expose
from flask_appbuilder.models.mongoengine.interface import MongoEngineInterface
class HelloWorld(BaseView):
route_base = "/hello"
@expose("/")
def hello(self):
return "Hello, World!"
@expose("/template")
def hello_template(self):
return render_template("hello.html")
appbuilder.add_view_no_menu(HelloWorld())
我们已经为路由添加了一个路径/hello/template
,并为此创建了一个单独的处理程序方法。注意,我们在return语句中使用了Flask显示模板方法。此外,请注意我们用于导入render_template
的Python语句。
转到项目的根目录并创建一个名为run.py
的文件,并在该文件中输入给定的代码片段。
注意,我们已经将端口更改为8080。如果需要,您可以在run.py
中更改端口。
from app import app
app.run(host='0.0.0.0', port=8080, debug=True)
现在开发服务器将在所有网络接口上运行,因为我们已经指定0.0.0.0
作为主机。
使用下面给出的命令启动开发服务器。记得激活虚拟环境。
python run.py
请注意,这是运行开发服务器的另一种方式。它为运行开发服务器提供了更大的灵活性,如果需要,我们可以使用定制的配置值。接下来,我们将始终使用相同的命令来运行开发服务器。
现在用浏览器访问IP:8080/hello/template
以查看浏览器中的输出。
Flask404处理器
Web应用程序通常有404处理。此处理程序显示一条自定义消息,并通知用户web应用程序没有特定的烧瓶模板或页面。让我们创建一个应用程序范围的控制器来发布一条自定义消息来覆盖这个用例。
修改view.py
文件如下:
from flask import render_template
from flask_appbuilder import ModelView, BaseView, expose
from flask_appbuilder.models.mongoengine.interface import MongoEngineInterface
from app import appbuilder
class HelloWorld(BaseView):
route_base = "/hello"
@expose("/")
def hello(self):
return "Hello, World!"
@expose("/template")
def hello_template(self):
return render_template("hello.html")
appbuilder.add_view_no_menu(HelloWorld())
@appbuilder.app.errorhandler(404)
def page_not_found(e):
"""
404 handler
"""
return render_template("404.html", base_template=appbuilder.base_template, appbuilder=appbuilder), 404
在错误处理程序中,Flask响应对象由return语句中返回的值组成。响应被修改为自定义返回代码404,而不是200。
在template
目录下创建或修改404.html
{% extends "appbuilder/base.html" %} {# Inherit from base.html #}
{% block content %}
{## double `{{` are used to interpolate variables ##}
<h2><center>{{_('Page not found')}}<center></h2>
<p>Please search the website for something else.</p>
{% endblock %}
上面代码中{# #}
用于写入注释,{{}}
用于变量、值或表达式。
现在我们启动服务器访问一个不存在的url,例如IP:8080/123
通过使用extend关键字继承base.html
模板的大部分内容,并且{% block content %}
帮助覆盖了base.html
模板的那部分内容。
Flask模板循环
然而,除了上面提到的符号外,我们还使用line语句
和{%
在模板中编写控制语句。让我们看看下面的例子。
修改hello world
视图,以理解更多的概念。打开views.py
并使用另一个方法更新HelloWorld
类,如下所示。
@expose("/greetings")
def hello_greetings(self):
greetings = [
'Good Morning',
'Good Afternoon',
'Good Evening',
'Good Night',
]
return render_template("hello.html", greetings=greetings)
修改hello.html
<!doctype html>
<html>
<h1>LiuYang</h1>
<p> Hello World! </p>
{% for item in greetings %}
{% if "Morning" in item %}
<i><p><b>{{item}}</b></p></i>
{% else %}
<p>{{item}}</p>
{% endif %}
{% endfor %}
</html>
之后启动服务器,访问IP:8080/hello/greetings
在上面的Flask模板中,我们使用了一个for循环来迭代列表中的项目。在我们的控制器或处理程序中,我们向模板传递了一个包含问候语值的列表。在模板内部,我们使用{{item}}语法访问每个项。
此外,if语句的用法也很重要。在这里,我们为Morning测试项目,并将其加粗和斜体。
Flask表单
现在让我们继续学习更多关于Flask表单的概念。
模板最重要的一个方面是获取用户的输入,并基于该输入编写后端逻辑。让我们创建一个表单。
我们使用Flask-Appbuilder SimpleFormView
来呈现表单。但是,让我们先创建一个表单。除了创建表单之外,我们还需要使用flask fab create-admin
命令来创建一个管理用户。
因此,在启动开发服务器之前使用该命令,以便随后创建的视图和表单可以通过登录用户进行验证。我们使用admin用户登录,并继续验证创建的视图是否在菜单下可见,如屏幕截图所示。
使用下面命令创建管理员
flask fab create-admin
除密码外其他内容均可设为默认(admin),密码设置为admin
输入python run.py
启动服务,访问IP:8080
点击右上角Login
登录系统输入刚刚创建的管理员账户admin
密码admin
即可登录。
在应用程序目录下创建一个名为forms.py
的文件,并将以下代码写入其中。
from wtforms import Form, StringField
from wtforms.validators import DataRequired
from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
from flask_appbuilder.forms import DynamicForm
class GreetingsForm(DynamicForm):
greeting1 = StringField(('Morning'),
description = ('Your morning Greeting'),
validators = [DataRequired()],
widget = BS3TextFieldWidget())
greeting2 = StringField(('Afternoon'),
description = ('Your Afternoon Greeting'),
validators = [DataRequired()],
widget = BS3TextFieldWidget())
greeting3 = StringField(('Evening'),
description = ('Your Evening Greeting'),
widget = BS3TextFieldWidget())
greeting4 = StringField(('Night'),
description = ('Your Night Greeting'),
widget = BS3TextFieldWidget())
我们已经基于Flask-Appbuilder
的DynamicForm
创建了一个表单。有四个文本字段。我们扩展问候示例。在这四个字段中,两个是必填字段,两个是可选字段,因为对于前两个问候,我们提到了验证器的值。
现在让我们为该表单创建一个视图。将以下这些代码行写入文件views.py中。
from flask import render_template, flash, redirect, url_for, session
from flask_appbuilder import SimpleFormView, ModelView, BaseView, expose, SimpleFormView
from app.forms import GreetingsForm
from app import appbuilder
class HelloWorld(BaseView):
route_base = "/hello"
@expose("/")
def hello(self):
return "Hello, World!"
@expose("/template")
def hello_template(self):
return render_template("hello.html")
@expose("/greetings")
def hello_greetings(self):
greetings = [
'Good Morning',
'Good Afternoon',
'Good Evening',
'Good Night',
]
return render_template("hello.html", greetings=greetings)
class GreetingsView(SimpleFormView):
form = GreetingsForm
form_title = 'This is a Greetings form'
message = 'Your Greetings are submitted'
def form_get(self, form):
form.greeting1.data = 'Your Morning Greeting'
form.greeting2.data = 'Your Afternoon Greeting'
form.greeting3.data = 'Your Evening Greeting'
form.greeting4.data = 'Your Night Greeting'
def form_post(self, form):
flash(self.message, 'info')
greetings = [
form.greeting1.data,
form.greeting2.data,
form.greeting3.data,
form.greeting4.data,
]
session['greetings'] = greetings
return redirect(url_for('HelloWorld.hello_greetings2'))
appbuilder.add_view_no_menu(HelloWorld())
appbuilder.add_view(
GreetingsView,
"Greetings View",
icon="fa-group",
category="My Forms",
category_icon="fa-cogs"
)
@appbuilder.app.errorhandler(404)
def page_not_found(e):
"""
404 handler
"""
return render_template("404.html", base_template=appbuilder.base_template, appbuilder=appbuilder), 404
在上面的视图中,我们有两种称为form_get
和form_post
的方法,用于分别填充表单字段中的默认值和在从浏览器提交表单后读取输入的值。
之后在直接添加导航栏My Forms
启动服务器,点开My Forms
下的Greeting View
,编辑完成之后记得点击保存。
我们还利用Flask会话对象将字段值存储在form_post
中,以便我们可以在将要编写的相应新视图中访问它们,之后我们创建映射。
class HelloWorld(BaseView):
## 其他方法
@expose("/greetings2")
def hello_greetings2(self):
greetings = session['greetings']
return render_template("hello.html", greetings=greetings)
再次启动服务器进入Greeting View
并点击保存
在此视图中,我们从会话对象中读取值,并使用Flask渲染模板在面向用户的HTML中显示这些值。请注意,hello_greetings2
是实现与hello_greetings
类似的相同功能的另一种方法。
唯一的区别是,使用hello_greetings2
可以显示用户输入的值,而在hello_greetings
中,我们在写入映射到相应路径的视图时未从用户那里获取任何输入并编码。
Flask响应
在代码中,您很少会看到对Flask响应的显式使用。Flask中的Response类只是Werkzueg的Response类的一个子类,而Werkzueg又继承了ResponseBase类的子类。
每当我们调用return语句或方法(如render_template
)时,Flask都会在内部形成Flask响应对象。
此外,如果需要,我们可以在视图的return语句中自定义响应代码和内容类型,如下面的修改过的HelloWorld
视图所示。
class HelloWorld(BaseView):
## 其他方法
@expose("/greetings2")
def hello_greetings2(self):
greetings = session['greetings']
return render_template("hello.html", greetings=greetings), 201,
{"Content-Type" : "application/json"}
Flask重定向
应用程序并非总是能够根据来自客户端的不同请求来预先定义响应。
在程序中,我们可以使用Flask Redirect
,在这种情况下,可以响应其他请求提供其他视图或位置可以实现的内容。我们将Flask Redirect
与标准HTTP
返回代码一起中止使用。
例如,在下面的代码中,我们对HTTP代码301
使用了重定向,而对401
使用了中止。
from flask import render_template, flash, redirect, url_for, session, abort
from flask_appbuilder import SimpleFormView, ModelView, BaseView, expose, SimpleFormView
from app.forms import GreetingsForm, LoginForm
from app import appbuilder
class HelloWorld(BaseView):
route_base = "/hello"
@expose("/")
def hello(self):
return "Hello, World!"
@expose("/template")
def hello_template(self):
return render_template("hello.html")
@expose("/greetings")
def hello_greetings(self):
greetings = [
'Good Morning',
'Good Afternoon',
'Good Evening',
'Good Night',
]
return render_template("hello.html", greetings=greetings)
@expose("/greetings2")
def hello_greetings2(self):
greetings = session['greetings']
return render_template("hello.html", greetings=greetings), 201, {"Content-Type" : "application/json"}
@expose("/success")
def success(self):
return "success"
class GreetingsView(SimpleFormView):
form = GreetingsForm
form_title = 'This is a Greetings form'
message = 'Your Greetings are submitted'
def form_get(self, form):
form.greeting1.data = 'Your Morning Greeting'
form.greeting2.data = 'Your Afternoon Greeting'
form.greeting3.data = 'Your Evening Greeting'
form.greeting4.data = 'Your Night Greeting'
def form_post(self, form):
flash(self.message, 'info')
greetings = [
form.greeting1.data,
form.greeting2.data,
form.greeting3.data,
form.greeting4.data,
]
session['greetings'] = greetings
return redirect(url_for('HelloWorld.hello_greetings2'))
class LoginView(SimpleFormView):
form = LoginForm
def form_get(self, form):
form.username.data = 'username'
form.password.data = 'password'
def form_post(self, form):
if form.username.data == 'admin':
return redirect(url_for('HelloWorld.success'))
else:
abort(401)
appbuilder.add_view_no_menu(HelloWorld())
appbuilder.add_view(
GreetingsView,
"Greetings View",
icon="fa-group",
category="My Forms",
category_icon="fa-cogs"
)
appbuilder.add_view(
LoginView,
"Login View",
icon="fa-group",
category="My Forms",
category_icon="fa-cogs",
)
@appbuilder.app.errorhandler(404)
def page_not_found(e):
"""
404 handler
"""
return render_template("404.html", base_template=appbuilder.base_template, appbuilder=appbuilder), 404
在视图中添加success
映射。
@expose("/success")
def success(self):
return "success"
表单修改为:
from wtforms import Form, StringField
from wtforms.validators import DataRequired
from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
from flask_appbuilder.forms import DynamicForm
class GreetingsForm(DynamicForm):
greeting1 = StringField(('Morning'),
description = ('Your morning Greeting'),
validators = [DataRequired()],
widget = BS3TextFieldWidget())
greeting2 = StringField(('Afternoon'),
description = ('Your Afternoon Greeting'),
validators = [DataRequired()],
widget = BS3TextFieldWidget())
greeting3 = StringField(('Evening'),
description = ('Your Evening Greeting'),
widget = BS3TextFieldWidget())
greeting4 = StringField(('Night'),
description = ('Your Night Greeting'),
widget = BS3TextFieldWidget())
class LoginForm(DynamicForm):
username = StringField(('username'),
description = ('Your username'),
validators = [DataRequired()],
widget = BS3TextFieldWidget())
password = StringField(('password'),
description = ('Your password'),
validators = [DataRequired()],
widget = BS3TextFieldWidget())
启动服务器,登录admin后选择Login Form
输入username:admin,password:xxx
输入``username:xxx,password:xxx`
Flask调试工具栏
在上一教程中,我们已经介绍了Flask的交互式调试器。在本教程中,我们将采取进一步的步骤来简化Flask应用程序的调试。安装后,Flask Debug
工具栏将在Flask应用程序上覆盖显示。
安装Flask Debugtoolbar(安装过程已完成,无需再次操作)
pip3 install flask-debugtoolbar
要激活调试工具栏,请在我们的项目中打开__init__.py
文件,并通过添加以下代码行来修改代码。
from flask_debugtoolbar import DebugToolbarExtension
app.debug = True
toolbar = DebugToolbarExtension(app)
请注意,Flask调试工具栏仅在调试模式下启用。启用后,当您重新加载应用程序时,将看到下面两种现象:
- 调试工具栏出现在浏览器的右侧。单击并展开它以查看工具栏提供的各种功能。
- 每当有新的POST请求发送到应用程序时,它就会被工具栏拦截,以便我们可以检查与应用程序调试有关的变量和其他参数。
可以在run.py中添加以下配置禁用此默认拦截。
app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False
测试Flask服务视图
我们需要组织测试代码以使其更易于管理。在目录中创建一个名为conftest.py
的文件,并将以下提到的行从test_hello.py
移至该文件,这个文件就算Pytest的配置文件。
from app import appbuilder
import pytest
@pytest.fixture
def client():
""" A pytest fixture for test client """
appbuilder.app.config["TESTING"] = True
with appbuilder.app.test_client() as client:
yield client
pytest模块在运行时由pytest加载。这些模块均可用,并与所有测试共享。在任何项目的根路径中定义conftest.py被认为是最佳实践,因为pytest可以识别项目中的所有模块而无需指定显式的PYTHONPATH。
为test_hello文件再添加一个测试。下面给出一个示例测试。我们调用客户端对象的get方法,并在resp.data
中存储的响应数据中声明期望值。
同样,您可以编写更多指向各种视图的测试。我们将在后续教程中编写更多测试。
def test_greetings(client):
""" A test method to test view hello_greetings"""
resp = client.get("/hello/greetings", follow_redirects=True)
assert b"Good Morning" in resp.data
使用以下命令运行测试
pytest -v test_hello.py
还没有出现失败。让我们再设计一个测试,如下所述。
def test_greetings2(client):
""" A test method to test view hello_greetings2 """
resp = client.get("/hello/greetings2", follow_redirects=True)
assert b"Good Morning" in resp.data
由于我们未在views.py
文件的HelloWorld
类中定义任何消息属性,因此该测试将失败。
使用pytest -v test_hello.py
运行测试后,与以下所示图片类似的结果将再次显示在控制台上。
实验总结
在本教程中,我们了解了模板如何在Flask框架中工作。我们概述了使用变量和表达式使用用户定义的值创建和渲染Flask模板的步骤。
我们还看到了Flask Appbuilder插件的预定义视图BaseView的示例。Flask开发人员可以轻松地将此视图子类化以创建自定义视图。
到目前为止所涵盖的概念可帮助读者使用Flask在没有数据库后端的情况下快速创建静态和动态网站。将在下一个教程中说明我们介绍将数据库与Flask结合使用时以及如何使用ModelView从数据库读取数据和向数据库写入数据。
实验2、Flask模板、表单、视图和重定向示例的更多相关文章
- VS2010 MFC中制作Visual Studio风格的停靠侧栏窗口(CDockablePane里嵌套FormView表单视图)
VS2010 MFC中制作Visual Studio风格的停靠侧栏窗口(CDockablePane里嵌套FormView表单视图) 1. 在资源窗口里新建一个FormView的Dialog,修改ID为 ...
- Angular11 模板表单、响应式表单(自定义验证器)、HTTP、表单元素双向绑定
1 模板表单 模型通过指令隐式创建 技巧01:需要在模块级别引入 FormsModule ,通常在共享模块中引入再导出,然后在需要用到 FormsModule 的模块中导入共享模块就可以啦 impor ...
- Flask 扩展 表单
pip install flask-wtf 一个简单的表单 from flask_wtf import Form from wtforms import StringField from wtform ...
- flask 单个表单多个提交按钮
单个表单多个提交按钮 在某些情况下,可能需要为一个表单添加多个提交按钮.比如在创建文章的表单中添加发布按钮和存草稿的按钮.当用户提交表单时,需要在视图函数中根据按下的按钮来做出不同的处理. 下面例子中 ...
- Flask入门 表单Flask-wtf form原生 Bootstrap渲染(七)
(1) 原生的表单 模板页面,form表单form.html <form action="{{ url_for('/check/') }}" method='post'> ...
- Flask:web表单
客户端发送的所有通过POST发出的请求信息都可以通过request.form获取.但是如果我们要生成表单的HTML代码和验证提交的表单数据那么就需要采用另外的方法.Flask-WTF扩展可以把处理we ...
- flask form表单验证
新建forms.py文件 #!/usr/bin/env python #-*-coding:utf--*- #导入模块 from flask_wtf import FlaskForm #FlaskFo ...
- flask 处理表单数据
处理表单数据 表单数据的处理涉及很多内容,从获取数据到保存数据大致有以下步骤: 1. 解析请求,获取表单数据 2. 对数据进行必要的转换,比如讲勾选框的值转换成python的布尔值 3. 验证数 ...
- flask之表单验证 2018.12.23
#flask的消息闪现依赖于flash库,用户发送的请求方式存储在request模块中 #跳转依赖于redirect模块,还可以通过url_for方法来进行方法上的寻址 from flask impo ...
随机推荐
- C++ Log日志系统
闲得无聊,瞎写的一个东西. 好多地方能够优化甚至可能重写,也没写,就记下了个思路在这里. 主要熟练一下C++17的内容. version = 0.1 lc_log .h 1 #pragma once ...
- 三、多线程之Thread与Runnable的区别
Thread与Runnable的区别(用三个窗口同时出售10张车票为例子) 运行代码 运行结果 分析 System.out.println("开始测试多线程");class MyT ...
- 仅用 CSS 实现多彩、智能的阴影
背景 有没有想过如何创建从前景元素中继承某些颜色的阴影效果?阅读本文并找出如何实现方法吧! 前几天我经过家得宝(Home Depot,美国家得宝公司,全球领先的家居建材用品零售商),他们正在大规模展销 ...
- K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题排查
问题现象 在TKE控制台上新建版本为v1.18.4(详细版本号 < v1.18.4-tke.5)的独立集群,其中,集群的节点信息如下: 有3个master node和1个worker node, ...
- Redis 集群伸缩原理
Redis 节点分别维护自己负责的槽和对应的数据.伸缩原理:Redis 槽和对应数据在不同节点之间移动 环境:CentOS7 搭建 Redis 集群 一.集群扩容 1. 手动扩容 (1) 准备节点 9 ...
- WM_PAINT 与 WM_ERASEBKGND消息的深入分析
当WM_PAINT消息不是由函数InvalidateRect产生的时(即通过最大话,最小化,移动,下拉菜单等),系统会先产生连续产生若干个WM_ERASEBKGND消息,紧接着在产生WM_PAINT消 ...
- .NET之API版本控制
1. 优点 有助于保护原有系统,不受影响,并及时修改问题 可以实现用户的私人定制(比如是付费接口) 快速迭代 2. API版本控制 在URL中追加版本或者作为查询字符串参数 通过自动以标头和通过接受标 ...
- 关于Aborted connection告警日志的分析
前言: 有时候,连接MySQL的会话经常会异常退出,错误日志里会看到"Got an error reading communication packets"类型的告警.本篇文章我们 ...
- WTM Blazor,Blazor开发利器
Blazor从诞生到现在也有一段时间了,之前一直在观望,从dotnet5中Blazor的进步以及即将到来的dotnet6中的规划来看,Blazor的前途还是光明的,所以WtmBlazor来了! Bla ...
- select执行顺序
先from 找到表on过滤 找到两张表有对应关系的记录按join的方式添加外部行where 过滤group by分组having 过滤select 从having 过滤出来的字段中选择需要的字段dis ...