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

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

myzbx 2025-03-05 19:30 51 浏览

一、概述

这是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 %}    
  

Sign In

{{ form.hidden_tag() }}

{{ form.username.label }}
{{ form.username(size=32) }}

{{ form.password.label }}
{{ form.password(size=32) }}

{{ form.remember_me() }} {{ form.remember_me.label }}

{{ form.submit() }}

{% endblock %}

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

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

以下将讲述HTML知识,上述这段HTML代码中:

标签用作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..label }需要的字段标签、{ form.() }需要的字段。对于需要其他HTML属性的字段,可将这些属性作为参数传递。此模板中的用户名、密码字段将size作为参数添加到这个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:导航栏中增加登录链接

Microblog: Home Login

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

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:基础模板中的闪烁消息

    
  
  {% if title %}
{{ title }} - Microblog
	{% else %}
    Welcome to Microblog
  {% endif %}
    
   
    	
Microblog: Home Login

{% with messages = get_flashed_messages() %} {% if messages %}
    {% for message in messages %}
  • {{ message }}
  • {% endfor %}
{% endif %} {% endwith %} {% block content %} {% endblock %}

上述代码中,使用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 %}
  

Sign In

{{ form.hidden_tag() }}

{{ form.username.label }}
{{ form.username(size=32) }}
{% for error in form.username.errors %} [{{ error }}] {% endfor %}

{{ form.password.label }}
{{ form.password(size=32) }}
{% for error in form.password.errors %} [{{ error }}] {% endfor %}

{{ form.remember_me() }} {{ form.remember_me.label }}

{{ form.submit() }}

{% endblock %}

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

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

2.7生成URL

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

Microblog: Home Login

登录视图函数还定义了传递给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()进行链接

...    
  
Microblog: Home Login
...

更新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

相关推荐

如何设计一个优秀的电子商务产品详情页

加入人人都是产品经理【起点学院】产品经理实战训练营,BAT产品总监手把手带你学产品电子商务网站的产品详情页面无疑是设计师和开发人员关注的最重要的网页之一。产品详情页面是客户作出“加入购物车”决定的页面...

怎么在JS中使用Ajax进行异步请求?

大家好,今天我来分享一项JavaScript的实战技巧,即如何在JS中使用Ajax进行异步请求,让你的网页速度瞬间提升。Ajax是一种在不刷新整个网页的情况下与服务器进行数据交互的技术,可以实现异步加...

中小企业如何组建,管理团队_中小企业应当如何开展组织结构设计变革

前言写了太多关于产品的东西觉得应该换换口味.从码农到架构师,从前端到平面再到UI、UE,最后走向了产品这条不归路,其实以前一直再给你们讲.产品经理跟项目经理区别没有特别大,两个岗位之间有很...

前端监控 SDK 开发分享_前端监控系统 开源

一、前言随着前端的发展和被重视,慢慢的行业内对于前端监控系统的重视程度也在增加。这里不对为什么需要监控再做解释。那我们先直接说说需求。对于中小型公司来说,可以直接使用三方的监控,比如自己搭建一套免费的...

Ajax 会被 fetch 取代吗?Axios 怎么办?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!今天给大家带来的主题是ajax、fetch...

前端面试题《AJAX》_前端面试ajax考点汇总

1.什么是ajax?ajax作用是什么?AJAX=异步JavaScript和XML。AJAX是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX可以使网页实...

Ajax 详细介绍_ajax

1、ajax是什么?asynchronousjavascriptandxml:异步的javascript和xml。ajax是用来改善用户体验的一种技术,其本质是利用浏览器内置的一个特殊的...

6款可替代dreamweaver的工具_替代powerdesigner的工具

dreamweaver对一个web前端工作者来说,再熟悉不过了,像我07年接触web前端开发就是用的dreamweaver,一直用到现在,身边的朋友有跟我推荐过各种更好用的可替代dreamweaver...

我敢保证,全网没有再比这更详细的Java知识点总结了,送你啊

接下来你看到的将是全网最详细的Java知识点总结,全文分为三大部分:Java基础、Java框架、Java+云数据小编将为大家仔细讲解每大部分里面的详细知识点,别眨眼,从小白到大佬、零基础到精通,你绝...

福斯《死侍》发布新剧照 "小贱贱"韦德被改造前造型曝光

时光网讯福斯出品的科幻片《死侍》今天发布新剧照,其中一张是较为罕见的死侍在被改造之前的剧照,其余两张剧照都是死侍在执行任务中的状态。据外媒推测,片方此时发布剧照,预计是为了给不久之后影片发布首款正式预...

2021年超详细的java学习路线总结—纯干货分享

本文整理了java开发的学习路线和相关的学习资源,非常适合零基础入门java的同学,希望大家在学习的时候,能够节省时间。纯干货,良心推荐!第一阶段:Java基础重点知识点:数据类型、核心语法、面向对象...

不用海淘,真黑五来到你身边:亚马逊15件热卖爆款推荐!

Fujifilm富士instaxMini8小黄人拍立得相机(黄色/蓝色)扫二维码进入购物页面黑五是入手一个轻巧可爱的拍立得相机的好时机,此款是mini8的小黄人特别版,除了颜色涂装成小黄人...

2025 年 Python 爬虫四大前沿技术:从异步到 AI

作为互联网大厂的后端Python爬虫开发,你是否也曾遇到过这些痛点:面对海量目标URL,单线程爬虫爬取一周还没完成任务;动态渲染的SPA页面,requests库返回的全是空白代码;好不容易...

最贱超级英雄《死侍》来了!_死侍超燃

死侍Deadpool(2016)导演:蒂姆·米勒编剧:略特·里斯/保罗·沃尼克主演:瑞恩·雷诺兹/莫蕾娜·巴卡林/吉娜·卡拉诺/艾德·斯克林/T·J·米勒类型:动作/...

停止javascript的ajax请求,取消axios请求,取消reactfetch请求

一、Ajax原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax。注意abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。停止javascript的ajax请求...