Flask的response和request对象
一、视图(V)
视图是一个应用对请求进行响应的函数。 Flask 通过模型把进来的请求 URL 匹配到 对应的处理视图。视图返回数据, Flask 把数据变成出去的响应。 Flask 也可以反 过来,根据视图的名称和参数生成 URL 。
返回值:其实返回值返回的都是一个响应对象,底层将返回的字符串包装成一个response对象。其实就是底层的Response父类的default-minetype设置成text/html类型,用户看到的就是网页内容。
1.response响应对象
视图函数的返回值会自动转换为一个响应对象。如果返回值是一个字符串,那么会被转换为一个包含作为响应体的字符串、一个 200 OK 的状态代码 和一个 text/html 类型的响应对象(response对象)。返回值的类型其实不只仅仅有字符串类型,还支持返回字典,元组,响应对象,WSGI回调函数(# The return type must be a string, dict, tuple, Response instance, or WSGI callable)。
-
如果视图返回的是一个响应对象,那么就直接返回它。
-
如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的 响应对象。
- 如果视图函数单纯返回"****"的字符串的话,flask会自动进行一些封装让他变成浏览器可以读取的格式,也就是content-type = text/html,状态码为200。
-
如果返回的是一个字典dict,那么它会被转换为一个 JSON 响应。
- 如果字典还不能满足需求,还需要创建其他类型的 JSON 格式响应,那么调用 jsonify()创建一个响应对象,该函数会序列化任何支持的 JSON 数据类型。。
-
如果返回的是一个元组,那么元组中的项目可以提供额外的信息。元组中必须至少 包含一个项目,且项目应当由 (response, status) 、 (response, headers) 或者 (response, status, headers) 组成。 status 的值会重载状态代码, headers 是一个由额外头部值组成的列表 或字典。
-
如果以上都不是,那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象。
如果想要在视图内部掌控响应对象的结果,也就是响应头的信息不满足你的需求,需要在响应头中添加额外的信息,或者想要返回一个字符串页面内容,那么可以使用 make_response() 函数。
假设有如下视图:
@app.errorhandler(404)
def not_found(error):
return render_template('error.html'), 404
可以使用 make_response() 包裹返回表达式,获得响应对象,并对该对象 进行修改,然后再返回:
@app.errorhandler(404)
def not_found(error):
resp = make_response(render_template('error.html'), 404)
resp.headers['X-Something'] = 'A value'
return resp
@app.route('/index4')
def index4():
content = '''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
<style>
div{
width: 100%;
height: 100px;
border: 2px solid red;
}
</style>
</head>
<body>
<h1>欢迎来到京东购物网站</h1>
<div>
<ul>
<li>hello</li>
<li>abc</li>
<li>world</li>
</ul>
</div>
</body>
</html>
'''
response = make_response(content) # 返回值就是一个response对象
# 定制响应头
response.headers['mytest'] = '123abc'
response.headers['myhello'] = 'hellohello'
# 将定制好的response返回
return response
总结:
(1)什么时候需要使用make_response?
当返回一个视图的时候,需要定制响应头,则可以使用make_response()包裹相应信息。
(2)response对象常用的属性与方法:
print(response.content_type)
print(response.headers)
print(response.status_code) # 200
print(response.status) # 200 OK
(3)视图函数的返回值,response响应:
- str 自动转成response对象
- dict json
- response对象 response对象
- make_response() response对象
- redirect() 重定向 302状态码
- render_template() 模板渲染 + 模板
2.request请求对象
只需要导入,通过from flask import request,导入之后可以获取对象的属性和方法。
属性:
print(request.headers) # request对象 对象访问属性,也可以调用方法
print(request.path)
print(request.full_path)
# 获取到提交的参数内容,例如get形式提交表单
# /register2?username=zhangsan&address=Beijing
print(request.base_url)
print(request.url)
跟请求方法相关的: 获取页面提交的内容
(1)get:
request.args 底层是字典的形式 主要获取get提交的请求参数
如果是get请求格式是这个样子的:/register2?username=zhangsan&address=Beijing
此时的username是form表单中表单元素的name值
print(request.args) # dict类型 d = {'a':'aaa','b':'7878'} d.get('b') 只能取到get请求的
print(request.args.get('username')) # 获取值
print(request.args.get('address'))
(2)post:
request.form 底层是字典的形式 主要获取post提交的请求参数
注意post提交必须在路由中进行设置,通过methods = [‘GET’,‘POST’]
按照此种形式:
@app.route('/register2', methods=['GET', 'POST'])
def register2(): # 获取页面提交的内容
....... 内容省略
获取数据:
print(request.form) # 如果请求方法是post,则需要通过request.form取值
print(request.form.get('username'))
print(request.form.get('address'))
二、模板
1.返回模板文件
如果想在视图函数中获取模板xxx.html的内容则通过render_template()
render_template(‘模板名称’) 返回值是一个字符串,主要是通过模板引擎(Jinjia2)去找到文件并将模板内容转成字符串的形式。
@app.route('/register')
def register():
return render_template('register.html') # 默认去模板文件夹中找模板文件。
疑问:怎么知道哪个文件时模板文件夹?
答:创建Flask对象的时候,有一个template_folder参数设置成了template,声明好了模板文件夹,就回去这里指定的文件里面找模板文件了。
2.重定向+url_for(路径反向解析)
redirect(‘/’)实现重定向,两次响应。但是如果在项目中,会有很多路径或者路径比较长,不易记,所以可以使用url_for来指定endpoint(在路由的装饰器上添加endpoint),去访问真正的路径。
url_for有两种使用方式:
- 如果路由有配置endpoint的话,那么url_for的参数直接使用endpoint来反向解析路径
- 如果没有配endpoint的话,就填写函数名,url_for会根据函数名去反向解析路径
- 注意:如果路由是注册在蓝图上的话,函数名前需要 指定蓝图的名字,以蓝图名.函数名的形式作为url_for的参数
@app.route('/', endpoint='index')
def index():
return render_template('index.html')
@app.route('/register', methods=['GET', 'POST'])
def register():
print(request.method)
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
repassword = request.form.get('repassword')
# 用户密码一致性验证
if password == repassword:
# 保存用户
user = {'username': username, 'password': password}
users.append(user)
return redirect(url_for('index')) # 有两次响应:1。302状态码 + location 2。 返回location请求地址内容
else:
return '两次密码不一致'
return render_template('register.html')
@app.route('/show')
def show():
# users[] ----> str'' json字符串
j_str = json.dumps(users)
return j_str
@app.route('/test')
def test():
url = url_for('index') # 路径反向解析
print(url) # /
return 'test'
3.模板的语法
如果需要传值到模板页面中,渲染在页面上显示的话,可以在视图函数中返回的时候添加参数,例如:return render_template(‘show_1.html’, girls=girls, users=users)
(1)在模板中获取view中传递的变量值:{{ 变量名}},类似于vue中使用mastache语法,也叫双大括号语法显示变量值。变量值支持字符串、列表、元组、set、对象等常用的数据类型
render_template(‘模板名字’,key=value,key=value)
@app.route('/show')
def show():
name = 'xzz' # str
age = 18 # int
friends = ['迪丽热巴', 'cr', '任嘉伦'] # list
dict1 = {'gift': 'YSL', 'gift1': '鲜花', 'gift2': '包包'} # dict
# 创建对象
girlfriend = Girl('cr', 'mz') # 自定义的类构建的类型:Girl对象
return render_template('show.html', name=name, age=age, gender='男', friends=friends, dict1=dict1, girl=girlfriend)
模板文件显示内容:
<div>用户信息展示</div>
<p>
用户名是:{{ name }} -- {{ age }} ---{{ gender }}
<br>
{{ friends.0 }}
<br>
{{ dict1.get('gift') }} --- {{ dict1.gift1 }}
<br>
{{ girl.gender }} -- {{ girl.name }} -- {{ girl.addr }}
</p>
模板文件获取值的一些简化写法:
{{ list.0 }} 同 {{ list[0] }}
{{ dict.key }} 同 {{ dict.get(key) }}
{{ girl.name }} 同 {{ 对象.属性 }}
(2)控制块:
if语句
{% if 条件 %}
{% endif %}
{% if 条件 %}
条件为True
{% else %}
条件为False
{% endif %}
<ul>
{% for girl in girls %}
{# |相当于过滤器 #}
{% if girl|length >= 3 %}
<li class="a"> {{ girl }}</li>
{% else %}
<li>{{ girl }}</li>
{% endif %}
{% endfor %}
</ul>
for语句
{% for 变量 in 可迭代的对象 %}
for循环要做的任务
{% endfor %}
for循环还有一个loop变量,通过该变量可以获取到数据的下标(序号):
loop.index 序号从1开始
loop.index0 序号从0开始
loop.revindex reverse 序号是倒着的
loop.revindex0
loop.first 布尔类型 是否是第一行
loop.last 布尔类型 是否是第二行
<table border="1" cellpadding="0" cellspacing="0" width="50%">
{% for user in users %}
<tr {% if loop.index == 3 %} style="background-color: deeppink" {% endif %}>
<td> {{ loop.index }}</td>
<td>{{ user.username }}</td>
<td>{{ user.password }}</td>
<td>{{ user.addr }}</td>
<td>{{ user.phone }}</td>
</tr>
{% endfor %}
</table>
4.过滤器
过滤器的本质就是函数,模板语法中过滤器:
{{ 变量名 | 过滤器(*args) }}
{{ 变量名 | 过滤器 }}
(1)常见的过滤器:
1.safe : 禁用转译
msg = '<h1>520快乐!</h1>'
return render_template('show_2.html', girls=girls, users=users, msg=msg)
不想让其转译:
{{ msg | safe }}
2.capitalize:单词的首字母大写
{{ n1 | capitalize }}
3.lower和upper
大小写的转换
4.title 一句话中每个单词的首字母大写
msg = 'She is a beautiful girl'
{{ msg | title}}
5.reverse 翻转
{{ n1 | reverse}}
6.format
{{ '%s is %d years old' | format('cr',18) }}
7.truncate 字符串截断
(2)list的操作:
{# 列表过滤器的使用 #}
{{ girls | first }}<br>
{{ girls | last }}<br>
{{ girls | length }}<br>
{#{{ girls | sum }} 整型的计算 #}
{{ [1,3,5,7,9] | sum }}<br>
{{ [1,8,5,7,3] | sort }}<br>
(3)dict
{% for v in users.0.values() %} ---->获取值
<p>{{ v }}</p>
{% endfor %}
<hr>
{% for k in users.0.keys() %} ----》获取键
<p>{{ k }}</p>
{% endfor %}
<hr>
{% for k,v in users.0.items() %} ---》获取键值
<p>{{ k }}---{{ v }}</p>
{% endfor %}
(4)自定义过滤器:
- 通过flask模块中的add_template_filter方法
a. 定义函数,带有参数和返回值
b. 添加过滤器 app.add_template_filter(function,name=‘’)
c. 在模板中使用: {{ 变量 | 自定义过滤器 }} - 使用装饰器完成
a. 定义函数,带有参数和返回值
b. 通过装饰器完成,@app.template_filter(‘过滤器名字’)装饰步骤一的函数
c. 在模板中使用: {{ 变量 | 自定义过滤器 }}
5.模板复用
(1)模板继承
需要模版继承的情况:
- 多个模板具有完全相同的顶部和底部
- 多个模板具有相同的模板内容,但是内容中有部分不一样
- 多个模板具有完全相同的模板内容
模板复用标签:
{% block 名字 %}
{% endblock %}
模板复用的实现思路: 1.定义父模板 2.子模板继承父模板
实现步骤:
-
父模板:
- 定义一个base.html的模板,也就是父模板
- 分析模板中哪些是变化的,变化的部分用模板的复用标签block做个占位。比如:{% block title %}父模板的title{% endblock %}
- 注意:样式和脚本 需要提前预留。否则在子页面中只有继承自父模板的样式以及脚本,加上子页面的样式和脚本无法生效。也就是说子页面只能在block标签里面添加style,script这些内容。
{% block mycss %}{% endblock %}
{% block myjs %}{% endblock %}
-
子使用父模板:
- {% extends ‘父模板的名称(html文件名)’ %}将父模板继承过来
- 找到对应的block(坑)填充,每一个block都是有名字的。
#如果需要引入外部文件,比如说引入样式文件,脚本文件等公共的文件。url_for默认只有一个参数,就是static,filename是关键字参数,必须要加上filename关键字才能成功访问。
<link rel="stylesheet" href="{{ url_for('static',filename='css/style.css') }}">
include: 包含,在A,B,C页面都共同的部分,但是其他页面没有这部分,这个时候考虑使用include。
步骤:
- 先定义一个公共的模板部分,xxx.html
- 谁使用则include过来, {% include ‘文件夹/xxx.html’ %}
6.宏:macro
(1)把它看作是jinja2的一个函数,这个函数可以返回一个HTML字符串
(2)目的:代码可以复用,避免代码冗余
定义两种方式:
- 在模板中直接定义:
{% macro 函数声明 %} 函数体 {% endmacro %}
{# 定义宏 #}
{% macro form(action,value='登录',method='post') %}
<form action="{{ action }}" method="{{ method }}">
<input type="text" placeholder="用户名" name="username">
<br>
<input type="password" placeholder="密码" name="password">
<br>
<input type="submit" value="{{ value }}">
</form>
{% endmacro %}
{# 调用宏 #}
{{ form('/') }}
- 将所有宏提取到一个模板中(macro.html),哪里需要哪里导入,推荐使用这种方式定义宏,维护性比较好。
{% import ‘macro.html’ as xxx %}
{{ xxx.宏名字(参数) }}
# 将需要复用的宏单独放到一个文件中,哪里需要就导入调用即可。
{% macro form(action,value='登录',method='post') %}
<form action="{{ action }}" method="{{ method }}">
<input type="text" placeholder="用户名" name="username">
<br>
<input type="password" placeholder="密码" name="password">
<br>
<input type="submit" value="{{ value }}">
</form>
{% endmacro %}
# 在其他文件中使用
{% import 'macro/macro.html' as func %}
{{ func.form('/welcome',value='注册') }}
三、总结
(1)变量: {{ 变量 }}
(2)块:
成对出现
{% if 条件 %} …{% endif %}
{% for 条件 %} …{% endfor %}
{% block 条件 %} …{% endblock %}
{% macro 函数声明 %} 函数体 {% endmacro %}
单个出现
{% include ‘’ %} 包含
{% import ‘’ %} 导入宏
{% extends ‘’ %}
函数/宏使用
{{ url_for(‘static’,filename=‘’) }}
{{ hongname(xxx) }}