Flask[寻觅]

Flask[寻觅]

七月 04, 2023

模板

作用

  1. HTML代码复用
  2. 网页内容动态显示

使用模板技术,可以将那些可共用的代码放在基础模板里,其他的网页可以继承这个基础模板,也可以用include的方式加载进来。

Jinja2

使用Jinja2模板引擎,通过render_template函数返回一个html文件

这些html文件默认存储在项目根目录下的tempates文件夹中,这个目录是可以自定义的,创建Flask对象时,通过template_folder来设置。

使用ajax技术,前端页面通过ajax向后端发送请求,后端返回用户的具体信息。使用模板,则可以不必使用ajax在前端请求数据,在使用render_template返回html时,会将用户的信息一起返回,由模板引擎根据用户的信息对html进行渲染,以达到相同的效果。

后端数据展示

后端的数据放到前端展示,下面是项目的结构示意

1
2
3
4
5
flask_demo/
├── __init__.py
├── app.py
└── templates
└── welcome.html

app.py 的内容

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


app = Flask(__name__)

@app.route('/welcome')
def welcome():
return render_template('welcome.html', name='小明')


app.run(port=5500)

welcome.html的内容

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>北京欢迎你</title>
</head>
<body>
<p>北京欢迎你,</p>
</body>
</html>

响应对象

是几个常见的视图返回数据的方式

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
from flask import Flask, jsonify, render_template, Response


app = Flask(__name__)

@app.route('/text')
def get_text():
return '返回文本'

@app.route('/dict')
def get_dict():
return {'state': 0}


@app.route('/json')
def get_json():
return jsonify({'state': 0})


@app.route('/html')
def get_html():
return render_template('index.html')

@app.route('/response')
def get_resonponse():
return Response('Not Found', status=404)

if __name__ == '__main__':
app.run(port=5566)
  1. get_text 返回一个字符串
  2. get_dict 返回一个字典
  3. get_json 返回一个json格式的字符串
  4. get_html 返回一个模板
  5. get_resonponse 返回一个Response对象

使用谷歌浏览器来查看这几个http响应的类型,1, 4, 5 的Content-Type 是 text/html,2,3的Content-Type 是application/json。这说明,flask会根据视图函数的返回值类型,来决定最终的Content-Type。

Response

Response是Flask中默认使用的响应对象

默认情况下设置为具有HTML mimetype。通常情况下,你不必自己创建这个对象,因为flask.flask.make_response会帮你处理这个问题。

如果你想替换所使用的响应对象,你可以将其子类化,并将 flask.flask.response_class设置为你的子类。

1. make_response

视图函数的返回值,最终会传递给make_response方法,在该方法中,对返回值的类型进行判断,并根据返回值的不同做相应的处理

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def make_response(self, rv):
status = headers = None

# unpack tuple returns
if isinstance(rv, tuple):
len_rv = len(rv)

# a 3-tuple is unpacked directly
if len_rv == 3:
rv, status, headers = rv
# decide if a 2-tuple has status or headers
elif len_rv == 2:
if isinstance(rv[1], (Headers, dict, tuple, list)):
rv, headers = rv
else:
rv, status = rv
# other sized tuples are not allowed
else:
raise TypeError(
"The view function did not return a valid response tuple."
" The tuple must have the form (body, status, headers),"
" (body, status), or (body, headers)."
)

# the body must not be None
if rv is None:
raise TypeError(
"The view function did not return a valid response. The"
" function either returned None or ended without a return"
" statement."
)

# make sure the body is an instance of the response class
if not isinstance(rv, self.response_class):
if isinstance(rv, (text_type, bytes, bytearray)):
# let the response class set the status and headers instead of
# waiting to do it manually, so that the class can handle any
# special logic
rv = self.response_class(rv, status=status, headers=headers)
status = headers = None
elif isinstance(rv, dict):
rv = jsonify(rv)
elif isinstance(rv, BaseResponse) or callable(rv):
# evaluate a WSGI callable, or coerce a different response
# class to the correct type
try:
rv = self.response_class.force_type(rv, request.environ)
except TypeError as e:
new_error = TypeError(
"{e}\nThe view function did not return a valid"
" response. The return type must be a string, dict, tuple,"
" Response instance, or WSGI callable, but it was a"
" {rv.__class__.__name__}.".format(e=e, rv=rv)
)
reraise(TypeError, new_error, sys.exc_info()[2])
else:
raise TypeError(
"The view function did not return a valid"
" response. The return type must be a string, dict, tuple,"
" Response instance, or WSGI callable, but it was a"
" {rv.__class__.__name__}.".format(rv=rv)
)

# prefer the status if it was provided
if status is not None:
if isinstance(status, (text_type, bytes, bytearray)):
rv.status = status
else:
rv.status_code = status

# extend existing headers with provided headers
if headers:
rv.headers.extend(headers)

return rv

从上面的代码中可以看到,当视图返回一个字典时,flask会自动使用jsonify进行转换。通常情况下,我们不会在视图中直接返回Response对象,因为make_response会帮我们将返回值封装成Response。但如果你自己相对返回值进行特殊处理,那么直接返回Response对象是可行的,就像get_resonponse函数那样操作。

↑ 以上均复制自COOLPYTHON

2. 利用Response返回图片

直接构造Response对象可以为接口返回图片数据

1
2
3
4
5
@app.route("/image")
def image():
f = open("static/public/pic/coolpython.png", 'rb')
resp = Response(f.read(), mimetype="image/jpeg")
return resp

若不直接创建Response对象,那么你就无法设置mimetype首部

3. 自定义Response

也可自定义Response类,进行个性化的处理,但这个类必须继承Response

1
2
3
4
5
6
7
8
class ImageResponse(Response):
default_mimetype = 'image/jpeg'

@app.route("/image")
def image():
f = open("static/public/pic/coolpython.png", 'rb')
resp = ImageResponse(f.read())
return resp

上面定义的ImageResponse类里,default_mimetype 就是image/jpeg, 因此在视图中创建ImageResponse对象时无需再设置mimetype。

4. 替换response_class

flask对象有一个response_class属性,默认是Response,可以将其替换成自定义的响应类。

假设需要实现一个图片服务,返回的数据都是图片,mimetype都是image/jpeg,那么可以这样做

1
2
3
4
5
6
7
8
9
10
class ImageResponse(Response):
default_mimetype = 'image/jpeg'

app = Flask(__name__)
app.response_class = ImageResponse

@app.route("/image")
def image():
f = open("static/public/pic/coolpython.png", 'rb')
return f.read()

在image函数,直接返回从图片中读取的数据返回,这些数据最终会被ImageResponse类处理。