python web Flask框架

Flask 教程:在 PyCharm 中创建 Flask 应用程序 | The PyCharm Blog (jetbrains.com)

*flask无法安装成功 出现Non-zero exit code(2)

解决方法:python -m pip install pip==20.0.2 -i https://pypi.tuna.tsinghua.edu.cn/simple

flask基础

搭建flask环境

安装python隔离环境 virtualenv

进入cmd 输入 pip install virtualenv

创建 python虚拟环境

virtualenv flask_env(环境名) (默认python解析器)

virtualenv –p Python解释器的路径 虚拟环境名(指定python解析器)

进入虚拟环境 .\ 环境名\Scripts\activate 前面有(flask_env)表示当前进入虚拟环境,且环境名为flask_env

屏幕截图 2023-09-22 210250

退出虚拟环境 deacitivaet

将环境依赖的包记录到 requirements.txt pip freeze > requirements.txt

image-20230922211409400

在env_space目录下会自动生成requirements.txt文件

image-20230922211536237

将requirements.txt 中记录的依赖包安装 pip install -r requirements.txt

安装flask pip install flask

在pycharm中创建虚拟环境

在界面中选择 创建新的project

image-20230922214307749

在下面选择之前创建好的虚拟环境中的python解析器

image-20230922214423761

创建好后terminal中前显示(flask_env) 创建的虚拟环境名 则为成功进入虚拟环境

image-20230922214707344

第一个flask程序

1
2
3
4
5
6
7
8
9
10
11
# 导入Flask类
from flask import Flask
# 实例化Flask类
app = Flask(__name__)
# 定义视图函数,并为该函数注册路由
@app.route("/") #路由
def hello_flask(): #视图函数
return "<p>Hello, Flask!</p>"
if __name__ == "__main__":
# 启动开发服务器
app.run()

程序的基本结构

Hello Flask程序包含3个比较重要的部分,分别是Flask类、开发服务器、路由与视图。

flask类

该类中封装了很多与Flask程序相关的方法,所有的Flask程序中必须要创建一个Flask类的对象

创建类对象

app = Flask(__name__)

必选参数

__name__:__name__是Flask中一个特殊的变量,用于保存程序主模块或者包的名称

可选参数

static_folder:用于指定存放静态文件的文件夹名称,默认值为static。

static_url_path:用于指定前端访问静态文件的路径,默认值为static_folder的名称。

template_folder:用于指定存放模板文件的文件夹名称,默认为应用程序根路径下的templates文件夹

开发服务器

app.run()

host:运行当前程序的主机名称,默认值为’127.0.0.1’或’localhost’。

port:运行当前程序的主机对应的端口号,默认值为5000。

debug:是否启用调试模式,默认值为False。

flask 配置信息

flask 扩展包

路由

路由的创建

1
2
3
4
app=Flask(__name__)
@appp.route(" / ")
def index():
return "hello world"
1
2
3
4
5
6
app=Flask(__name__)
def index_new():
return "hello world"
app.add_url_rule(rule='/',view_func=index_new )
#rule 必选参数表示url
#view_func 可选参数 表示相关联的视图函数名

url传递参数

参数传递的方式

1
2
3
4
app=Flask(__name__)
@appp.route("/<参数名> ")
def index(参数名):
return "hello world"

转换器(限定参数类型)

内置转换器

<类型 : 参数名> 转换器不写类型默认为string

string

int

float

any 匹配给定的一系列值中的某一个元素

path 与string类似匹配非空字符串,允许包含’/‘

uuid

自定义转换器

form werkzeug.routing import BaseConverter 导包

class 类名(BaseConverter): 自定义规则

regx 正则表达式匹配规则

to_python()方法: 用于将url中的参数的类型转换为视图函数中的类型

to_url()方法: 用于将python数据类型转换为url中使用的字符串类型

app.url_map.converters[“自定义转换器名称”]=自定义转换器类名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

自定义参数类型(自定义转换器)

- 背景:
- 如果系统提供的int,float,等参数类型满足不了需求的时候,我们需要自定义
- 之所以,int,float,path可以接收不同的数据类型,是因为,系统已经提供好对应的转换器了
- 自定义转换器格式
- 1.定义类,继承自BaseConverter
- 2.重写init方法
- 3.初始化父类成员变量, 还有子类自己的规则
- 4.将转换器类,添加到系统默认的转换器列表中

需求: 接收三位整数


from flask import Flask
from werkzeug.routing import BaseConverter

app = Flask(__name__)


# - 1.定义类,继承自BaseConverter
class MyRegexConverter(BaseConverter):
#这样直接指定规则,不够灵活,具体应该匹配什么规则应该交给路由
# regex = "\d{3}"

# - 2.重写init方法,接收两个参数
def __init__(self,map,regex):
# - 3.初始化父类成员变量, 还有子类自己的规则
super(MyRegexConverter, self).__init__(map)
self.regex = regex

# - 4.将转换器类,添加到系统默认的转换器列表中
app.url_map.converters["re"] = MyRegexConverter

#打印输出所有的系统转换器列表
print(app.url_map.converters)

#匹配三位整数
#使用re('规则'),实际上是传递了两个参数,参数1: app.url_map, 参数2:括号中写的正则规则
@app.route('/<re("\d{3}"):number>')
def hello_world(number):

return "the three number is %s"%number

#匹配四位整数
@app.route('/<re("\d{4}"):number>')
def get_four_number(number):

return "the four number is %s"%number

#匹配一个手机号
@app.route('/<re("1[3-9]\d{9}"):phone>')
def get_phone_number(phone):

return "the phone number is %s"%phone

if __name__ == '__main__':
app.run(debug=True)

Flask URL默认转换器、自定义转换器、to_python、to_url_flask 转码_海洋的渔夫的博客-CSDN博客(https://blog.csdn.net/u012887259/article/details/103070911/)

处理请求

请求的方式

get 从服务器中请求资源

post 向服务器提交表单或上传文件

put 更新数据

option 客户端查看服务器功能

flask对请求的处理流程:

接受请求 -> 创建请求上下文 -> 请求上下文入栈 -> 创建该请求的应用上下文 -> 应用上下文入栈 -> 处理逻辑 -> 请求上下文出战 -> 应用上下文出栈

请求钩子

(判断用户身份的通用函数,避免视图函数重复写)

  • before_first_request 处理第一个请求前运行
  • before_request 每次请求前运行
  • after_request 处理逻辑没有异常,在每次请求后运行
  • teardown_request 在每次请求后运行 不管异常
  • teardown_appcontext 在应用上下文从栈中弹出之前与运行
  • after_this_request 在视图函数内创建,在请求执行后,该函数接收响应对象为参数
1
2
3
@app.before_request
def before_request():
return "这是请求钩子before_request注册的函数"

上下文

临时保存程序运行过程中一些信息

请求上下文

request:封装请求信息

属性 说明
args 获取URL中的请求参数
methods 获取请求的HTTP方法
cookies 获取包含Cookie名称和值的字典对象
data 获取包含字符串形式的请求数据
form 获取解析后的表单数据
values 一个包含form和args全部内容的CombineMultiDict
headers 获取首部字段
user_agent 获取浏览器标识信息
方法 说明
close() 关闭当前请求
get_data() 获取请求中的数据
get_json() 作为JSON解析并返回数据
make_form_data_parse() 创建表单数据解析器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from flask import Flask, request
#通过request.args.get("key")来获取url中的参数 get/post请求都行
#通过request.form.get("key")来获取url中的参数 只能是post请求

app = Flask(__name__)

@app.route('/index/get',methods=['get'])
def user():
method=request.method
args_id=request.args.get('id')
form_id=request.form.get('id')
return f"args获取:{args_id} form获取:{form_id}"

if __name__ == '__main__':
app.run()

image-20230924131515190

flask框架中request请求用法详解_flask request_IT之一小佬的博客-CSDN博客

Flask request 属性详解_flask.request_爱人BT的博客-CSDN博客

session:记录请求会话中的用户信息

  • 当服务器收到浏览器发送的请求时,会检查浏览器中是否包含名称为session的Cookie信息,如果不存在,那么浏览器会认为当前请求是一个新会话,并生成一个名称为session的信息存储到浏览器的Cookie中。
  • 浏览器在下一次请求服务器时,将携带Cookie中存储的session信息,此时服务器通过浏览器提交的session信息便可以辨别出当前请求属于哪个浏览器。
方法 说明
get(key) 通过传入的key值,获取Cookie中对应的value值
pop(key) 通过传入的key值,删除Cookie中对应的value值
items() 将Cookie中的值以“key:value”形式返回
values() 获取Cookie中所有的value值
clear() 清空当前站点Cookie中的内容
keys() 获取Cookie中所有的key值
update() 接收一个字典,根据接收的字典更新或添加到Cookie中
应用上下文

current_app对象

表示当前激活的Flask应用程序实例

g对象

表示程序的一个全局临时变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from flask import Flask, g
app = Flask(__name__)
@app.route('/')
def get_user():
user_id = '001' # 设置用户id
user_name = 'flask' # 设置用户名称
g.user_id = user_id # 将用户id保存到g对象中
g.user_name = user_name # 将用户名称保存到g对象中
result = db_query()
return f'{result}'
def db_query():
user_id = g.user_id # 使用g对象获取用户id
user_name = g.user_name # 使用g对象获取用户名称
return f'{user_id}:{user_name}'

处理响应

响应报文

​ 浏览器发出的请求会触发相应的视图函数,并将视图函数的返回值作为响应体,之后生成完整的响应内容,即响应报文。响应报文主要由4个部分组成,分别是状态行、响应报头、空行以及响应体。

image-20230924110912411

​ 状态码由三位数字组成,其中第1位数字表示响应的类别,它的取值为1~5

状态码 说明
100~199 表示服务器成功接收部分请求,要求客户端继续提交其余请求才能完成整个处理过程
200~299 表示服务器成功接收请求并已完成整个处理过程,常用的状态码为200,表示请求成功
300~399 为完成请求,客户需进一步细化请求。例如,请求的资源已经移动到一个新地址,常用状态码包括302(表示所请求的页面已经临时转移至新的URL)、307和304(表示使用缓存资源)
400~499 客户端的请求有错误,常用状态码包括404(表示服务器无法找到被请求的页面)和403(表示服务器拒绝访问,权限不够)
500~599 服务器端出现错误,常用状态码为500,表示请求未完成,服务器遇到不可预知的情况

生成响应

​ Flask内部自动会该字符串转换成Response类的对象。在Flask中,Response类表示响应,它封装了响应报文的相关信息。如果希望在Flask程序中主动生成响应,一般可以通过Response类的构造方法或make_response()函数实现。

Response类

Response(response, status, headers, mimetype, content_type, direct_passthrough)

  • response:可选参数,表示视图函数返回的响应体。
  • status:可选参数,表示响应状态码。
  • headers:可选参数,表示响应报头。
  • mimetype:可选参数,表示响应体的MIME类型。
  • content_type:可选参数,表示响应体的类型。
1
2
3
4
5
6
7
8
9
10
11
from flask import Flask, Response
app = Flask(__name__)
@app.route('/index')
def index():
# 使用Response类的构造方法生成响应对象,设置状态码为201,响应类型为text/html
resp = Response(response='Python&Flask',status=201,
content_type='text/html;charset=utf-8')
return resp
if __name__ == '__main__':
app.run()

make_response()函数

1
2
3
4
5
6
7
8
9
10
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/index')
def index():
res = make_response('Python&Flask',201,
{'content-type':' text/html;charset=utf-8'})
return res
if __name__ == '__main__':
app.run()

make_response() 生成json类型

1
2
3
4
5
6
7
8
9
from flask import Flask, make_response, jsonify
app = Flask(__name__)
@app.route('/response')
def resp():
res = make_response(jsonify({'Python':'Flask'}),202)
return res
if __name__ == '__main__':
app.run()

URL反向解析

flask.url_for模块中提供了URL反向解析的函数url_for(),该函数可以根据视图函数的名称获取对应的URL。

url_for(endpoint, values,_external,_scheme,_anchor,_method,**values)

  • endpoint:必选参数,表示反向解析的端点(用于标记一个视图函数以及对应的URL规则)名称,默认值为视图函数名。
  • values:可选参数,表示URL地址传递的参数。
  • _external:可选参数,表示是否供程序外部使用,默认值为False,若为True,则返回绝对URL地址,例如,http://127.0.0.1:5000/hello/flask。
1
2
3
4
5
6
7
8
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/hello/flask')
def greet():
return f"{url_for('greet')}" # 反向解析视图函数greet()对应的URL
if __name__ == '__main__':
app.run()

image-20230924122922761

若URL规则中包含要传递的参数,则调用url_for()函数时需要将该参数以关键字参数形式传递。

1
2
3
4
5
6
7
8
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/hello/<name>')
def greet(name):
return f"{url_for('greet',name=name)}"
if __name__ == '__main__':
app.run()

image-20230924123159632

使用url_for()函数反向解析URL时,除了传递URL规则中的参数以外,还可以传递任何额外参数给URL地址的参数。

1
2
3
4
5
6
7
8
9
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/hello_1/<name>')
def greet(name):
# 将age=20添加到URL地址中
return f"{url_for('greet',name=name, age=20)}"
if __name__ == '__main__':
app.run()

image-20230924123422113

页面重定向

在Flask程序中,页面重定向功能可以通过redirect()函数实现。

redirect(location, code=302, Response=None)

  • location:必选参数,表示重定向的URL地址。
  • code:可选参数,表示重定向状态码,默认状态码为302。
  • Response:可选参数,表示实例化响应时使用的Response类,若未指定默认使用的响应类为werkzeug.wrappers.Response。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from flask import Flask,session,request,redirect,url_for

app=Flask(__name__)
app.secret_key='your_secret_ke&^52@!' #设置secret_key的值
@app.route('/index')
def index():
if 'username' in session:
return f'你好{session.get("username")}'
return redirect(url_for(login)) # 页面重定向到登录页面
@app.route('/login',methods=['post','get'])
def login():
if request.method=='POST':
session['username']=request.form['username']
return redirect(url_for(index)) # 页面重定向到index页面
return '''
<form method="post">
<p><input type=text name=username>
<p><input type=submit value='登录'>
</form>
'''
if __name__=="__main__":
app.run()

补充:abort()函数

1
2
3
4
5
from flask import Flask,abort
....
def abort_1():
return abort(404) #页面返回状态码404
...

模板

创建/渲染模板

1.在根目录下创建templates文件夹

2.创建html文件(创建模板就是创建html文件)

3.创建flask程序导入render_template()函数

4.在视图函数中渲染模板

render_template() 函数

render_template("template_name_or_list,**context")

  • template_name_or_list 必选参数,表示要加载的模板名称
  • context 可选参数表示像模板文件传递的参数,以关键字参数传递

image-20230924135545320

image-20230924135523838

image-20230924135553796

基础语法

模板变量

1
**{{ 变量名 }}!**

jinja2能是识别所有类型的变量,如:字符串、列表、字典,它们会被替换为同名的 Python 变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask,render_template

app=Flask(__name__) #flask实例

@app.route('/2') #创建路由
def index(): #视图函数
string = 'www.imooc.com'
list = ['www', 123, (1, 2, 3), {"name": "poloyy"}]
dict = {'name': 'zhangsan', 'age': True}
return render_template('index.html',string=string, list=list, dict=dict)
#前面的name对应模板变量的名称,后面为视图定义的变量name

if __name__=="__main__":
app.run()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
{{ string }}

<ul>
<li> {{ list[0] }}
<li> {{ list[1] }}
<li> {{ list[2] }}
<li> {{ list[3] }}
</ul>

<ul>
<li> {{ dict['name'] }}
<li> {{ dict['age'] }}
</ul>
</html>

image-20230924143227385

过滤器

变量的值可以使用过滤器修改。过滤器添加在变量名之后,二者之间以竖线分隔,一个模板可以使用多个过滤器,多个之间用竖线(” | “)隔开

1
{{变量名|filter(parameter)}}

内置过滤器

过滤器 说明
abs() 返回给定参数的绝对值
random() 返回给定列表中的一个随机元素
safe() 将变量值标记为安全,保证渲染时不进行转义
tojson() 将给定参数序列化为JSON字符串
escape() 用HTML安全序列替换字符串中的字符&、<、>、’和”
length() 返回变量值的长度
sort() 对变量保存的数据进行排序,该函数内部调用的是Python的sorted()函数
过滤器 说明
join() 使用指定符号将字符串中的字符进行拼接,默认符号为空字符串
int() 将值转换为整数,如果转换不起作用,返回0
float() 将值转换为浮点数,如果转换不起作用,返回0.0
capitalize() 将变量值的首字母改为大写字母,其余字母改为小写字母
trim() 清除变量值前后的空格
upper() 将变量值转换为大写字母
1
2
3
4
5
6
@app.route('/filters')
def use_of_filters():
num = -2.3
li = [2, 1, 5, 6, 7, 4, 4]
string = 'flask'
return render_template('filters.html', num=num, li=li, string=string)
1
2
3
4
5
6
7
8
9
10
<body>
{#返回变量num的绝对值#}
<h4>绝对值:{{ num|abs }}</h4>
{#将变量num转换为整型#}
<h4>转换为整型:{{ num|int }}</h4>
{#返回变量li中随机的一个元素#}
<h4>获取随机元素:{{ li|random }}</h4>
{#返回变量li的长度#}
<h4>获取列表长度:{{ li|length }}</h4>
</body>

image-20230925083452546

自定义过滤器

通过@app.template_filter()将自定义的函数添加到注册到过滤器列表中,可选参数name表示过滤器名称,默认为被装饰的函数名

1
2
3
4
5
6
7
@app.template_filter()
def xxxx(data):
return xxx

@app.template_filter() # 注册自定义过滤器
def custom_filters(data): # 自定义过滤器
return data[::-1] #反转列表元素

选择结构

1
2
3
4
5
6
7
8
9
10
选择结构语句必须由{%endif%}结尾

{% if 条件语句1 %}
语句1
{% elif 条件语句2 %}
语句2
……
{% else %}
语句n
{% endif %}
1
2
3
4
@app.route('/query-score/<int:score>')
def query_score(score):
return render_template('select_struct.html',score=score)

1
2
3
4
5
6
7
8
9
10
11
<body>
{% if score >= 85 %}
优秀
{% elif 75 <= score < 85 %}
良好
{% elif 60 <= score < 75 %}
中等
{% else %}

{% endif %}
</body>

image-20230925083819721

循环结构

其中for标识循环结构的起始位置,endfor标识循环结构的结束位置,且两者都不能省略。

1
2
3
{% for 临时变量 in 变量 %}
语句
{% endfor %}
1
2
3
4
@app.route('/goods')
def goods():
goods_name = ['洗碗机','电饭锅','电烤箱','电磁灶','微波炉']
return render_template('loop_struct.html', goods_name=goods_name)
1
2
3
4
5
6
7
8
9
10
<body>
{% for good in goods_name %}
<h4>{{ good }}</h4>
{% endfor %}

<!--不换行输出列表-->
{% for good in lst %}
{{ good }}
{% endfor %}
</body>

image-20230925084042505

循环结构的特殊变量

变量 说明
loop.index 当前循环的迭代数(从1开始计数)
loop.index0 当前循环的迭代数(从0开始计数)
loop.revindex 当前反向循环的迭代数(从1开始计数)
loop.revindex0 当前反向循环的迭代数(从0开始计数)
loop.first 若当前循环为第一次循环,则返回True
loop.last 若当前循环为最后一次循环,则返回True
loop.length 当前序列包含的元素数量
loop.previtem 上一个迭代的数据
loop.nextitem 下一个迭代的数据

宏的定义与调用

宏将模板中的重复代码进行封装,需要时调用宏

宏的定义

1
2
3
{% macro 宏的名称(参数列表) %}
宏内部的逻辑代码
{% endmacro %}

在macro.html文件的标签中,定义一个描述input控件类型的宏。

1
2
3
4
{% macro inputstyle(name, value='', type='checkbox') %}
<input name="{{ name }}" value="{{ value }}" type="{{ type }}">
{% endmacro %}

宏的调用

1
{{ 宏的名称(参数列表)}} 
1
2
3
4
5
6
<h4>您目前对哪些技术感兴趣?</h4>
<p>{{ inputstyle('Python') }} Python</p>
<p>{{ inputstyle('Java') }} Java</p>
<p>{{ inputstyle('big_data') }} 大数据</p>
<p>{{ inputstyle('JavaScript') }} JavaScript</p>
<p>{{ inputstyle('commit', value="提交", type="button") }}</p>
1
2
3
4
@app.route('/macro')
def input_style():
return render_template('macro.html')

image-20230925084929598

在多个模板中定义宏,将宏写入单独文件,再导入

1
2
3
4
5
6
7
8
{%import '宏文件的路径' [ as 宏的别名 ]%}
{%from '宏文件的路径' import 宏的名称 [ as 宏的别名 ]%}
例:
<body>
{% from 'macro.html' import inputstyle %}
<p>导入宏</p>
<p>{{ inputstyle('Python') }} Python</p>
</body>

image-20230925085046110

消息闪现

​ 用户反馈信息的方式——消息闪现,消息闪现会在请求完成后记录一条消息,之后在下一次请求向用户提示这条消息。例如,用户在登录页面输入错误的密码后,单击登录按钮才会在页面提示密码错误的消息。

flash() 函数

​ flash()函数执行后不会立即在浏览器页面中为用户弹出一条消息,而是需要在模板中通过get_flashed_messages()函数获取消息,并将其显示到页面中。

​ flash()函数会将发送的消息存储到session中,因此我们需要在程序中设置secret_key。

1
flash(message, category='message')
  • message:必选参数,发送闪现的消息。
  • category:可选参数,消息的类别。该参数支持4种取值,分别是message、error、info和warning,其中message是默认值,表示任何类型的消息;error表示错误的消息;info表示信息消息;warning表示警告消息。

get_flashed_messages() 函数

1
get_flashed_messages(with_categories=False , category_filter=())
  • with_categories:可选参数,表示是否同时返回消息与消息类别,若设置为True,则会以元组形式返回消息和消息类别;若设置为False,则只会返回消息。
  • category_filter:可选参数,表示只返回与消息类别一致的消息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from flask import Flask, render_template
from flask import flash, redirect, session, request, url_for
app=Flask(__name__)
app.secret_key = 'Your_secret_key&^52@!' # 设置secret_key
@app.route('/home')
def home_page():
username = session.get('username')
# 判断session是否存储username的数据,存在响应页面,不存在重定向login页面
if 'username' in session:
#渲染模板,将username传入到模板变量
return render_template('home_page.html', username=username)
return redirect(url_for('login')) # 重定向到login页面
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
if request.form['username'] != 'admin' or \
request.form['password'] != '123':
flash('用户名或密码错误', category='error') #密码错误通过消息闪现反馈给用户
else:
session['username'] = request.form['username']
session['password'] = request.form['password']
flash('恭喜您,登录成功', category='info')
return redirect(url_for('home_page'))
return render_template('login.html')
if __name__=="__main__":
app.run()
1
2
3
4
5
6
7
8
9
home_page
<body>
<h2>主页</h2>
<!--调用get_flashed_messages()函数获取了类别为info的消息,并使用for..in.. 遍历每条消息-->
{% for message in get_flashed_messages(category_filter = ('info')) %}
<span>{{ message }}</span>
{% endfor %}
<p>欢迎用户:{{ username }}</p>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
login
<body>
<h2>用户登录</h2>
{% for message in get_flashed_messages(category_filter = ('error')) %}
<p class="error" style="color: red;">{{ message }}</p>
{% endfor %}
<form action="" method="post" class="form">
<span>用户名:</span><br>
<input type="text" name="username"><br>
<span>密码:</span><br>
<input type="password" name="password"><br>
<p><input type="submit" value="登录"></p>
</form>
</body>

静态文件加载

​ 默认情况下静态文件都存储在与项目文件同级目录的static文件夹中,该文件夹需要由开发人员创建。

1
2
3
4
5
<!--、解析图片文件flask.png的URL规则为/static/test.png-->
url_for('static',filename='test.png ')

<img src="{{ url_for('static', filename='test.png') }}">

加载图片文件

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>加载图片</p>
<img src="{{ url_for('static',filename='test.jpg') }}">
</body>
</html>
1
2
3
@app.route('/static-file')
def load_staticfile():
return render_template('base.html')

加载css文件

1
2
3
4
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="{{ url_for('static',filename='Italics.css') }}">
</head>

模板的继承

​ 网页中的通用内容和样式,可以通过模板继承实现,包含通用内容和样式的模板称为基模板,继承基模板的模板称为子模板。

​ 模板继承通过block和extends实现,其中block用于标识与继承机制相关的代码块,extends用于指定子模板所继承的基模板。子模板通过继承可以获取基模板中的内容和结构

1
{% extends 基模板名称 %}  <!--{% extends %}必须位于子模板的第一行-->

image-20230926103758121

如果想要在子模板中仍然呈现基模板中的内容,那么可以使用super()函数实现

1
2
3
4
5
{% extends 'base.html' %}
{% block content %}
<p>我是子模板</p>
{{ super() }}
{% endblock %}

表单与类视图

通过Flask-WTF处理表单

​ Flask-WTF的表单其实是一个继承FlaskForm的类,表单类中可以根据需要包含若干个属性,每个属性的值又是一个表单字段类的对象,不同字段类的对象会映射为表单中的不同控件。

1
pip install flask-wtf   #安装flask-wtf

使用flask-wtf创建表单

WTForms库的Field类派生了许多表单字段类,常用字段类与表单控件的映射关系。

字段类 表单控件 说明
RadioField 一组单选按钮
SelectField 下拉列表
SubmitField 提交按钮
StringField 文本字段
PasswordField 密码文本字段
TextAreaField < textarea> 多行文本字段
HiddenField 隐藏文本字段

​ 常用的字段类都继承自WTForms库的Field类,所以我们可以通过Field类的构造方法实例化所有字段类,虽然有的字段类内部已经重写了Filed类的构造方法,但这些字段类的构造方法中会包含一些相同的参数。

​ 参数render_kw的值是一个字典,用于为表单控件设置一些属性,包括提示信息(placeholder)、高度(height)、宽度(width )、是否获得焦点(autofocus )等

参数 说明
label 字段标签
render_kw 字典类型,用于设置控件的属性
validators 列表类型,包含一系列的验证器,在提交表单数据时,会被列表中的验证器逐一验证
default 字符串或可调用对象,为表单字段设置默认值

​ 参数validators的值是一个列表,该列表中包含了一系列用于验证表单数据是否有效的验证器,只有当表单数据满足验证器的规则时,填写的表单数据才能成功提交到服务器。

​ 在WTForms库中,验证器是一些用于字段数据的Python类,这些类都封装在wtforms.validators模块中,因此我们在使用验证器之前需要先从wtforms.validators模块中导入相应的类。

验证器 说明
DataRequired(message=None) 验证数据是否有效,空字符串是为无效数据
Email(message=None) 验证数据是否为电子邮件地址
EqualTo(fieldname,message=None) 验证两个字段值是否相同
IPAddress(ipv4=True, ipv6=False, message=None) 验证数据是否为有效IP地址
Length(min=-1,max=-1,message=None) 验证输入值的长度是否在给定的范围内
NumberRange(min=None,max=None,message=None) 验证输入的数字是否在给定的范围内
验证器 说明
Optional(strip_whitespace=True) 无输入值时跳过其他验证
Regexp(regex,flags=0,message=None) 使用正则表达式验证输入值
URL(require_tld=True,message=None) 验证URL
AnyOf(values, message=None, values_formatter=None) 确保输入值在可选值列表中
NoneOf(values, message=None, values_formatter=None) 确保输入值不在可选值列表中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from flask import Flask
from flask_wtf import FlaskForm
from wtforms import StringField,PasswordField,SubmitField
from wtforms.validators import DataRequired,Length,EqualTo

app=Flask(__name__) #flask实例

#定义了表单类,包含四个属性,username,password,password2,submit,对应的值为StringField,PasswordField,SubmitField的类对象,通过validators参数指定验证器,用于限制输入框的输入内容。

class RegisterForm(FlaskForm):
username=StringField(label='用户名:',
validators=[DataRequired(message='用户名不能为空'),
Length(3,15,message='长度应该为3~15个字符 ')])
password=PasswordField('密码:',
validators=[DataRequired(message='用户名不能为空')])
password2=PasswordField('确认密码',
validators=[DataRequired(message='用户名不能为空'),
EqualTo('password',message='两次密码不一致')])
submit=SubmitField('注册')

@app.route('/') #创建路由
def first_flask(): #视图函数
return "hello flask"

if __name__=="__main__":
app.run()

在模板中渲染表单

​ 首先需要在视图函数中将表单类的对象传递到模板文件中,然后在模板文件中获取表单字段,将表单字段渲染到HTML页面进行呈现。

1
2
3
4
5
6
app.secret_key = '34sdfji9453#$@'

@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
return render_template('register_wtf.html', form=form)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
<h1>注册页面</h1>
<form method="post">
{{ form.csrf_token }}
<span>{{ form.username.label }}</span>
<br> {#获取username对应的标签名称#}
{{ form.username }}
<br> {#调用表单字段渲染为HTML#}
<span>{{ form.password.label }}</span>
<br>
{{ form.password }}
<br>
<span>{{ form.password2.label }}</span>
<br>
{{ form.password2 }}
<br>
<p>{{ form.submit }}</p>
</form>

</body>

使用flask_wtf验证表单

类视图

标准类视图

​ 标准类视图是继承flask.views模块中基类View的子类,该子类中必须重写View类中的dispatch_request()方法。除dispatch_request()方法外,我们也可以根据需要向View子类中添加其他方法或属性。

​ add_url_rule()方法将类视图与URL进行映射,不过该方法的view_func参数不能直接传入类视图的名称,而是需要传入通过as_view()方法将类视图转换后的视图函数。

  • lmethods属性:设置当前类视图可以处理的请求方式列表。
  • ldecorators属性:为类视图指定装饰器列表,该列表中可以放置一个或多个装饰器。
  • ldispatch_request()方法:用于实现处理不同HTTP请求的具体逻辑,该方法可以通过关键字参数接收URL传递的参数。
  • las_view()方法:用于将类转换为可与路由系统一起使用的实际视图函数。as_view()方法必须传入一个name参数,用于指定动态生成视图函数的名称,也可以根据需要传入一些位置参数和关键字参数,这些参数都会转发给类的构造方法,以创建类的实例,并调用类内部的dispatch_request()方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask, request
from flask.views import View
class MyView(View): # 定义类视图
methods = ['GET', 'POST'] # 指定请求方式
def dispatch_request(self, name): # 重写dispatch_request()方法
if request.method == 'GET':
return f'hello {name}'
app = Flask(__name__)
# 将类视图与URL规则进行映射
app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview'))
if __name__ == '__main__':
app.run()

基于方法类试图

​ 基于方法的类视图需要继承flask.views模块中的MethodView类,而MethodView类继承View类,由于MethodView类中已经重写了dispatch_request()方法,所以定义基于请求方法的类视图时不需要重写dispatch_request()方法

​ 在基于方法的类视图中,并非通过类属性methods来指定当前视图可以处理的请求方式,而是通过定义与请求方式同名的方法来处理不同的请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from flask.views import MethodView
from flask import Flask, render_template, request
class LoginView(MethodView):
def get(self): # 处理GET请求
return render_template('login.html')
def post(self): # 处理POST请求
username = request.form.get('username') # 获取输入的用户名
password = request.form.get('password') # 获取输入的密码
if username =='flask' and password == '123': # 判断用户名和密码是否为123
return f'用户:{username}登录成功。'
else:
return '用户名或密码错误,请重新登录。'
app = Flask(__name__)
app.add_url_rule('/login', view_func=LoginView.as_view('login'))
if __name__=="__main__":
app.run()

1
2
3
4
5
6
7
8
9
10
<body>
<form action="" method=post>
<span>用户名:</span><br>
<input type=text name=username><br>
<span>密码:</span><br>
<input type=password name=password><br>
<p><input type=submit value=登录></p>
</form>
</body>

蓝图

  • 将一个应用程序分解成一组子模块。这是大型应用程序的理想选择,即项目实例化一个应用实例,初始化一些扩展,以及注册一组蓝图。
  • 以一个URL前缀或子域在应用程序中注册蓝图。URL前缀或子域的参数成为该蓝图中所有视图函数的通用视图参数(具有默认值)。
  • 在一个应用程序中用不同的URL规则多次注册一个蓝图。
  • 通过蓝图提供模板过滤器、静态文件、模板和其他实用程序。蓝图不必实现应用程序或视图的功能。
  • 在初始化Flask 扩展时在应用程序中注册一个蓝图

image-20230927090507216

创建蓝图
1
flask.Blueprint(name, import_name, static_folder=None, static_url_path=None, template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, root_path=None, cli_group=<object object>)
  • name:必选参数,表示蓝图的名称。
  • import_name:必选参数,表示蓝图包的名称,通常为__name__。
  • static_folder:可选参数,表示静态文件夹的路径。
  • static_url_path:可选参数,表示静态文件的URL。
  • template_folder:可选参数,表示模板文件夹路径。
  • url_prefix:可选参数,表示附加到所有蓝图URL的路径,用于与Flask应用程序的其他URL区分
1
2
3
4
5
6
7
8
9
10
from flask import Blueprint
# 创建蓝图
user = Blueprint('user', __name__)
@user.route('/login')
def login():
return 'user_login'
@user.route('/register')
def register():
return 'user_register'

1
2
3
4
5
6
7
8
9
10
from flask import Blueprint
# 创建蓝图
admin = Blueprint('admin', __name__)
@admin.route('/login')
def login():
return 'admin_login'
@admin.route('/add')
def add():
return 'admin_add'

注册蓝图

register_blueprint()方法用于将蓝图注册到Flask程序中

1
register_blueprint(blueprint, url_prefix, subdomain, url_defaults,**options)
  • blueprint:必选参数,表示要注册的蓝图。
  • url_prefix:可选参数,表示附加到所有蓝图URL的路径,若在Blueprint类设置参url_prefix,则会被该参数值覆盖
1
2
3
4
5
6
7
8
from admin import admin
from user import user
from flask import Flask
app = Flask(__name__)
app.register_blueprint(admin, url_prefix='/admin') # 将蓝图admin进行注册
app.register_blueprint(user, url_prefix='/user') # 将蓝图user进行注册
if __name__ == '__main__':
app.run()

数据库操作