百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

三、flask博客项目实战-之表单

liuian 2025-04-26 19:25 54 浏览

一、概述

这是Flask Mega-Tutorial系列的第三部分,我将告诉你如何使用Web表单。

在第二章中我为应用主页创建了一个简单的模板,并使用诸如用户和用户动态的模拟对象。在本章中,我将解决这个应用程序中仍然存在的众多遗漏之一,那就是如何通过Web表单接受用户的输入。

Web表单是所有Web应用程序中最基本的组成部分之一。 我将使用表单来为用户发表动态和登录认证提供途径。

在继续阅读本章之前,确保你的microblog应用程序状态和上一章完结时一致,并且运行时不会报任何错误。

二、Flask-WTF简介和安装

在Flask中,处理应用程序中的Web表单,将使用到Flask-WTF扩展库,它是Flask和WTForms的简单集成,主要功能有:使用CSRF(Cross-site request forgery,译作 跨站请求伪造)令牌保护表单、文件上传、支持reCAPTCHA(译作 反全自动区分计算机和人类的图灵测试,简单点就是:验证码)。扩展是Flask生态系统中非常重要的一部分。今后还会需要更多的扩展。

我将使用Flask-WTF插件来处理本应用中的Web表单,它对WTForms进行了浅层次的封装以便和Flask完美结合。这是本应用引入的第一个Flask插件,但绝不是最后一个。插件是Flask生态中的举足轻重的一部分,Flask故意设计为只包含核心功能以保持代码的整洁,并暴露接口以对接解决不同问题的插件。

Flask插件都是常规的Python三方包,可以使用pip安装。 那就继续在你的虚拟环境中安装Flask-WTF吧:
进入虚拟环境中,安装Flask-WTF:

(venv) [root@python blog]# pip3 install flask-wtf

将附带安装WTForms,因为它是Flask-WTF的一部分。

2.1配置 configuration

目前为止,这个应用程序足够简单,无需担心它的配置。Flask(以及Flask扩展)在如何执行操作方面提供了很多自由,并需要做一些决定,并将这些决定作为一个配置变量列表传递给框架。

应用程序 有多种格式可指定配置选项。最基本的方案:在app.config这个字典中,将定义的变量作为键。形如:

app = Flask(__name__)app.config['SECRET_KEY'] = 'I am a secret, you can't guess.'#需要的话,可继续添加更多的变量

尽管上述语法可为Flask成功创建配置选项,但根据 关注点分离原则(Separation of concerns, SoC),所以不要将配置放在创建应用程序的相同位置,而是:将配置保存在单独的.py文件中,并使用类存储配置变量,将该.py文件放在项目顶级目录下。
/microblog/config.py:密钥配置

import os
class Config:
				SECRET_KEY = os.environ.get('SECRET_KEY') or 'you will never guess'

SECRET_KEY这个配置变量,将会被Flask及其扩展使用其值作为加密秘钥,用于生产签名或令牌。而Flask-WTF使用它来保护Web表单来免受CSFR攻击。

密钥的值 是具有两个术语的表达式,由or运算符连接。第一个术语是查找环境变量的值;第二个术语是一个硬编码的字符串。当然这个安全性还是很低的。当将应用程序部署在生产服务器上时,得设置一个安全级别高的。

其中os.environ是获取本机系统的各种信息(如环境变量等,你打印出来就明白了,哈哈),它是一个字典。我觉得os.environ.get('SECRET_KEY')在开发环境中并没有用,是None,不知部署后是什么。
有了上述这个配置文件,接下来得让Flask应用程序读取并应用它。在创建Flask应用程序实例后,就用app.config.from_object()方法完成:
app/__init__.py:Flask配置

from flask import Flask
from config import Config                #从config模块导入Config类


app = Flask(__name__)
app.config.from_object(Config)

from app import routes

查看刚才配置的密钥是什么:

(venv) [root@python blog]# python3
Python 3.9.0 (default, Oct 16 2020, 10:57:11)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
 >>> 

2.2用户登录表单

Flask-WTF扩展使用Python类来表示Web表单。表单类只是将表单的字段定义为类变量。

再次根据SoC(关注点分离原则),新建一个forms.py模块来存放Web表单类。在此,定义一个用户登录表单,要求用户输入用户名、密码,还包含“Remember Me”复选框、提交按钮。
app/forms.py:用户登录表单

from flask_wtf import FlaskForm        #从flask_wtf包中导入FlaskForm类
from wtforms import StringField,PasswordField,BooleanField,SubmitField       #导入这些类
from wtforms.validators import DataRequired 

class LoginForm(FlaskForm):    
					username = StringField('Username', validators=[DataRequired()])    
					password = PasswordField('Password', validators=[DataRequired()])    
					remember_me = BooleanField('Remember Me')
					submit = SubmitField('Sign In')

在Flask生态下,Flask扩展一般都使用flask_\这样的命名约定作为在模块中顶级导入的符号。在这个情况下,Flask-WTF的所有符号都在flask_wtf下,这也是FlaskForm基类在app/forms.py顶部导入的地方。

from wtforms import StringField,PasswordField,BooleanField,SubmitField

这条语句表示:这个用户登录表单的字段类型的4个类是直接从WTForms包导入的,因为Flask-WTF扩展是不提供自定义(字段类型?)版本。对于每个字段,将在LoginForm类中将对象创建为类变量。每个字段都有一个描述或标签作为第一个参数。

在某些字段中看到的可选参数validators将验证行为附加到字段中,如用户名、密码肯定是需要进行验证的。DataRequired验证器 只是简单地检查该字段不会提交为空。当然还有其他的验证器可用。

2.3用户登录-表单模板

有了上一步的登录表单,接下来得将表单添加到HTML模板中,让其在网页上呈现。
LoginForm类中定义的字段知道如何将自己渲染为HTML。
app/templates/login.html:用户登录表单模板

{% extends "base.html" %}
{% block content %}    
  <h1>Sign In</h1>
 			<form action="" method="post" novalidate>
        {{ form.hidden_tag() }}
				<p>
         {{ form.username.label }}<br>
          {{ form.username(size=32) }}
            </p>
				<p>
              {{ form.password.label }}<br>
              {{ form.password(size=32) }}
          </p>
					<p>
            {{ form.remember_me() }}
            {{ form.remember_me.label }}
							</p>
						<p>
                {{ form.submit() }}
             </p>
					</form>
{% endblock %}

这个用户登录表单模板使用了extends继承语句继承base.html模板,以确保一致的布局,即基础模板包含了所有页面的顶部导航栏。

在此的用户登录表单模板期望从LoginForm类实例化的表单对象作为参数给出,这个参数将由登录视图函数(目前还未编写)发送。

以下将讲述HTML知识,上述这段HTML代码中:<form>标签用作Web表单的容器。
其中action属性表示用于告知浏览器当用户在表单中输入信息提交时应使用的URL。该属性设置为空字符串时,表单将提交到当前位于地址栏的URL,即在页面上呈现表单的URL。
method属性用于指定在将表单提交到服务器时应使用的HTTP请求方法。默认情况下,是通过GET请求发送它。但几乎在所有情况下,使用POST请求会获得更好的用户体验,因为此类请求可在请求正文中提交表单数据,GET请求将表单字段添加到URL,会让浏览器地址栏变得混乱。
novalidate属性用于告知浏览器不对此表单中的字段运用验证,这有效地将此任务留给服务器中运行的Flask应用程序。当然,使用novalidate完全是可选的,但对于第一种形式,设置它是很重要的,因为这将允许在本章后面的测试服务器端验证。
form.hidden_tag()这个模板参数 生成一个隐藏字段,其中包括用来防止CSRF攻击的令牌。要使表单受保护,需要做的是包含此隐藏字段,并在Flask配置中定义的SECRET_KEY变量。
写过HTML Web表单的同学可能会发现这个模板中没有HTML字段,这是因为表单对象中的字段知道如何将自己呈现(渲染)为HTML,需要做的就是{ form.<field_name>.label }需要的字段标签、{ form.<field_name>() }需要的字段。对于需要其他HTML属性的字段,可将这些属性作为参数传递。此模板中的用户名、密码字段将size作为参数添加到<input>这个HTML标签作为属性。这还是可将CSS类、或ID附加到表单字段的方法。

2.4用户登录-表单视图

在编写完上一步的用户登录表单模板后,想要在浏览器中看到此表单的最后一步是:在应用程序中编写一个它的视图函数,用于渲染该模板。

因此,编写一个映射到/login URL的视图函数login(),并将其传递给模板进行渲染。在routes模块中增加代码:

app/routes.py:用户登录视图函数

from flask import render_template
from app import app
from app.forms import LoginForm

#...
 
@app.route('/login')
def login():
				form = LoginForm()          #表单实例化对象
        return render_template('login.html', title='Sign In', form=form)

上述视图函数很简单,从forms.py模块中导入LoginForm类,然后实例化该类,最后将其发送到模板。form=form,return中将form实例对象赋值给form变量,这将获得表单字段所需的全部内容。

为了便于访问登录表单,在基础模板中改进,即在导航栏中包含指向它的链接:
app/templates/base.html:导航栏中增加登录链接

<div>   
  	Microblog:    
      			<a href="/index">Home</a>
						<a href="/login">Login</a>
</div>

此刻,运行应用程序就可浏览器中查看该表单了。效果:图略

2.5接收表单数据

尝试点击上述“Sign In”提交按钮,浏览器将出现405错误“Method Not Allowed”。图略

在上一步中,用户登录的视图函数执行了一半的工作,即可在网页上显示表单。但它没有处理用户提交的数据的逻辑。这是Flask-WTF让这项逻辑处理变得非常简单的优势。更新用户登录视图函数代码,它接受、验证用户提交的数据:
app/routes.py:接收登录凭据

from flask import render_template,flash,redirect 
@app.route('/login',methods=['GET','POST'])
def login():
				form = LoginForm()
				if form.validate_on_submit():
          				flash('Login requested for user {},remember_me={}'.format(form.username.data,form.remember_me.data))
									return redirect('/index')
				return render_template('login.html',title='Sign In',form=form)

在@app.routes()装饰器中参数methods作用是:告诉Flask这个视图函数接受GET和POST请求方法,覆盖默认值(即只接受GET请求)。HTTP协议中,GET请求是将信息返回给客户端(如浏览器)的请求,到目前为止,该应用程序中的所有请求都属于这种类型;POST 请求通常在浏览器上服务器提交表单数据时使用。上述出现“Method Not Allowed”,是因为浏览器尝试发送POST请求,而应用程序没有配置去接受它。

form.validate_on_submit()方法完成所有表单处理工作。当浏览器发送GET接收带有表单的网页请求时,此方法将返回False,此时函数会跳过if语句并直接在函数的最后一行呈现模板。
当用户在浏览器按下提交按钮时,浏览器发送POST请求,form.validate_on_submit()将收集所有数据,运行附加到字段的所有验证器,如果一切正常,它将返回True,表明数据有效且可由应用程序处理。但如果至少有一个字段未通过验证,则函数就会返回False,接着就像上述GET请求那样。
当form.validate_on_submit()返回True,这个登录视图函数将调用两个函数,分别是flash()、redirect(),均从flask包导入的。
flash() 用于向用户显示消息,如让用户知道某些操作是否成功。目前为止,将使用其机制作为临时解决方案,因为暂无用户登录
真实所需的基础结构,此时只是显示一条消息用于确认应用程序已收到凭据。
redirect()用于指示客户端(浏览器)自动导航到作为参数给出的其他页面(如上述代码中的/index页面,即重定向到应用程序的/index页面)。

当调用flash()函数时,Flask会存储该消息,但闪烁的消息不会神奇地出现在Web页面中。应用程序的模板需要以适用于站点布局的方式呈现/渲染这些闪烁的消息。因此,将这些消息添加到基础模板中,以便所有模板都继承此功能。更新基础模板:
app/templates/base.html:基础模板中的闪烁消息

<html>    
  <head>
  {% if title %}
<title>{{ title }} - Microblog</title>
	{% else %}
    <title>Welcome to Microblog</title>
  {% endif %}
    </head>
   <body>
    	<div>
      			Microblog:
              <a href="/index">Home</a>
   						<a href="/login">Login</a>
   			</div>
   			<hr>
      {% with messages = get_flashed_messages() %}
      {% if messages %} 
        <ul>
          {% for message in messages %}
       					<li>{{ message }}</li>
    			{% endfor %}
          </ul>
          {% endif %}
          {% endwith %}
          {% block content %}
          {% endblock %}
    </body>
   </html>

上述代码中,使用with结构将调用get_flashed_messages()的结果分配给变量messages,都在模板的上下文。这个get_flashed_messages()函数来自Flask,并返回flash()之前已注册的所有消息的列表。接着if语句判断messages是否具有某些内容,在这种情况下,一个ul标签被渲染成每个消息作为一个li标签列表项。而这种渲染风格看起来不太好,但Web应用程序样式化的主题将在稍后出现。

这些闪烁的消息的一个有趣属性是:一旦通过get_flashed_messages()请求它们,它们就会从列表中删除,因此它们在flash()调用后只出现一次。

运行程序,再次测试表单是如何工作的。确保将用户名或密码字段为空来提交表单,以查看DataRequired验证器如何暂停提交过程。

用户名或密码为空时提交表单,网页没反应。都不为空时,随意输入。
图略

点击Sign in按钮后,倒是出现了一条消息:Login requested for user 123456@qq.com,remember_me=Flase
图略

2.6增强字段验证

附加到表单字段的验证器可防止无效数据接受到应用程序中。应用程序处理无效表单输入的方式是重新显示表单,让用户进行必要的更正。

当提交无效数据时,却没有明显提示用户提交的数据有问题,只是重新返回表单,这将影响用户体检。因此,现在的任务是:通过在验证失败的每个字段傍边增加有意义的错误提示来改善用户体验。

实际上,表单验证器已经生成了这些描述性错误消息,因此,缺少的是在模板中用于渲染/呈现它们的一些额外逻辑。在用户登录模板的用户名、密码字段中添加字段验证消息:更新代码
app/templates/login.html:提示字段验证错误消息

{% extends "base.html" %}
{% block content %}
  <h1>Sign In</h1>
 				<form action="" method="post" novalidate>
          {{ form.hidden_tag() }}
						<p>
                  {{ form.username.label }}<br>
                   {{ form.username(size=32) }}<br>
                   {% for error in form.username.errors %}
                    <span style="color:red;">[{{ error }}]</span>
										{% endfor %}
              </p>
               <p>
                    {{ form.password.label }}<br>
                    {{ form.password(size=32) }}<br>
                    {% for error in form.password.errors %}
                    <span style="color:red;">[{{ error }}]</span>
										{% endfor %}
                 </p>
                  <p>
                   {{ form.remember_me() }}
                   {{ form.remember_me.label }}
										</p>
										<p>
                      {{ form.submit() }}
                     </p> 
</form>
{% endblock %}

上述代码中,只是在用户名、密码字段之后添加for循环,以红色字体消息渲染错误消息。一般规则下,任何附加验证器的字段都会通过form.<field_name>.errors添加错误消息。这将是一个列表,因为字段可以附加多个验证器,并且多个可能提供错误消息提示给用户。

如果尝试提交空用户名或密码的表单,将看到红色错误提示,效果:图略

2.7生成URL

用户登录表单现在比较完整了,下面将学习在模板包含链接和重定向的方法。 例:基础模板中的当前导航栏

<div>     
  Microblog:
    <a href="/index">Home</a>
		<a href="/login">Login</a>
</div>

登录视图函数还定义了传递给redirect()函数的链接:

@app.route('/login', methods=['GET', 'POST'])
def login():
				form = LoginForm()
				if form.validate_on_submit():
        # ...
        return redirect('/index')
					# ...

直接在模板、源文件中编写链接的一个问题是:如果将来某天要重新组织链接,将不得不修改整个应用程序的这个链接,搜索、替换。

为更好地控制这些链接,Flask提供了一个名为 url_for()函数,它使用URL的内部映射到视图函数来生成URL。例:url_for('login')返回/login;url_for('index')返回/index。url_for()中的参数就是端点名称,也就是视图函数的名字。

使用函数名称而不是URL的优点:URL比视图函数名称更可能发生变化;某些URL很可能包含动态组件,手动生成这URL需要连接多个元素,这极易出错,而url_for()能生成这些复杂的URL。

因此,今后每次应用程序要生成URL时,都使用url_for()。
更新基础模板中的代码:
app/templates/base.html:使用url_for()进行链接

...    
  <div>
  Microblog:
    <a href="{{ url_for('index') }}">Home</a>
     <a href="{{ url_for('login') }}">Login</a>
    </div>
       ...

更新login()视图函数中的代码:
app/routes.py:对链接使用url_for()函数

from flask import render_template, flash, redirect, url_for 

# ...
  
@app.route('/login', methods=['GET', 'POST'])

def login():   
				form = LoginForm()
				if form.validate_on_submit():
        # ... 
        return redirect(url_for('index'))
				# ...

目前为止,项目结构:

microblog/
      venv/
      app/
      templates/
      					base.html
                index.html
                login.html
      __init__.py
      forms.py 
      routes.py
microblog.py

相关推荐

怎样解除自动关机模式(怎样解除自动开关机)

1、打开手机主界面,找到系统自带的“时钟”应用,点击打开它。2、点击进入时钟后,点击右下角的“计时器”。3、进入到计时器后,点击“在计时结束启用雷达”这个选项。4、然后在这里,下拉到最下面,勾选“停...

电脑最高配置是什么配置2025

一,2023最新主流电脑装机配置如下。二,处理器可以使用十二代的i512400或者i512490f,内存16gb双通道,显卡rtx3060,主板可以使用b660m或者h610m。三,如果十三代酷睿...

MySQL慢查询优化:从explain到索引,DBA手把手教你提升10倍性能

数据库性能是应用系统的生命线,而慢查询就像隐藏在系统中的定时炸弹。某电商平台曾因一条未优化的SQL导致订单系统响应时间从200ms飙升至8秒,最终引发用户投诉和订单流失。今天我们就来系统学习MySQL...

一文读懂SQL五大操作类别(DDL/DML/DQL/DCL/TCL)的基础语法

在SQL中,DDL、DML、DQL、DCL、TCL是按操作类型划分的五大核心语言类别,缩写及简介如下:DDL(DataDefinitionLanguage,数据定义语言):用于定义和管理数据库结构...

闲来无事,学学Mysql增、删,改,查

Mysql增、删,改,查1“增”——添加数据1.1为表中所有字段添加数据1.1.1INSERT语句中指定所有字段名语法:INSERTINTO表名(字段名1,字段名2,…)VALUES(值1...

数据库:MySQL 高性能优化规范建议

数据库命令规范所有数据库对象名称必须使用小写字母并用下划线分割所有数据库对象名称禁止使用MySQL保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来)数据库对象的命名要能做到见名识意,...

下载工具合集_下载工具手机版

迅雷,在国内的下载地位还是很难撼动的,所需要用到的地方还挺多。缺点就是不开会员,软件会限速。EagleGet,全能下载管理器,支持HTTP(S)FTPMMSRTSP协议,也可以使用浏览器扩展检测...

mediamtx v1.15.2 更新详解:功能优化与问题修复

mediamtxv1.15.2已于2025年10月14日发布,本次更新在功能、性能优化以及问题修复方面带来了多项改进,同时也更新了部分依赖库并提升了安全性。以下为本次更新的详细内容:...

声学成像仪:泄露监测 “雷达” 方案开启精准防控

声学成像仪背景将声像图与阵列上配装的摄像实所拍的视频图像以透明的方式叠合在一起,就形成了可直观分析被测物产生状态。这种利用声学、电子学和信息处理等技术,变换成人眼可见的图像的技术可以帮助人们直观地认识...

最稳存储方案:两种方法将摄像头接入威联通Qu405,录像不再丢失

今年我家至少被4位邻居敲门,就是为了查监控!!!原因是小区内部监控很早就停止维护了,半夜老有小黄毛掰车门偷东西,还有闲的没事划车的,车主损失不小,我家很早就配备监控了,人来亮灯有一定威慑力,不过监控设...

离岗检测算法_离岗检查内容

一、研发背景如今社会许多岗位是严禁随意脱离岗位的,如塔台、保安室、监狱狱警监控室等等,因为此类行为可能会引起重大事故,而此类岗位监督管理又有一定困难,因此促生了智能视频识别系统的出现。二、产品概述及工...

消防安全通道占用检测报警系统_消防安全通道占用检测报警系统的作用

一、产品概述科缔欧消防安全通道占用检测报警系统,是创新行业智能监督管理方式、完善监管部门动态监控及预警预报体系的信息化手段,是实现平台远程监控由“人为监控”向“智能监控”转变的必要手段。产品致力于设...

外出住酒店、民宿如何使用手机检测隐藏的监控摄像头

最近,一个家庭在他们的民宿收到了一个大惊喜:客厅里有一个伪装成烟雾探测器的隐藏摄像头,监视着他们的一举一动。隐藏摄像头的存在如果您住在酒店或民宿,隐藏摄像头不应再是您的担忧。对于民宿,房东应报告所有可...

基于Tilera众核平台的流媒体流量发生系统的设计

曾帅,高宗彬,赵国锋(重庆邮电大学通信与信息工程学院,重庆400065)摘要:设计了一种基于Tilera众核平台高强度的流媒体流量发生系统架构,其主要包括:系统界面管理模块、服务承载模块和流媒体...

使用ffmpeg将rtsp流转流实现h5端播放

1.主要实现rtsp转tcp协议视频流播放ffmpeg下载安装(公认业界视频处理大佬)a、官网地址:www.ffmpeg.org/b、gitHub:github.com/FFmpeg/FFmp…c、推...