1.flask基础知识
1.1 flask框架介绍
Flask相比于django更加的轻量级,最核心的两个模块: Werkzeug(路由模块),模板引擎则使用 Jinja2。Flask不像django自带各种模块,用于一些小中型的项目开发。
Flask诞生于2010年,是Armin ronacher(人名)用 Python 语言基于 Werkzeug 工具箱编写的轻量级Web开发框架。
Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAlchemy),都需要用第三方的扩展来实现。比如可以用 Flask 扩展加入ORM、窗体验证工具,文件上传、身份验证等。Flask 没有默认使用的数据库,你可以选择 MySQL,也可以用 NoSQL。
其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。
官网: https://flask.palletsprojects.com/en/1.1.x/
官方文档: http://docs.jinkan.org/docs/flask/
Flask常用第三方扩展包:
- Flask-SQLalchemy:操作数据库,ORM;
- Flask-script:终端脚本工具,脚手架;
- Flask-migrate:管理迁移数据库;
- Flask-Session:Session存储方式指定;
- Flask-WTF:表单;
- Flask-Mail:邮件;
- Flask-Bable:提供国际化和本地化支持,翻译;
- Flask-Login:认证用户状态;
- Flask-OpenID:认证, OAuth;
- Flask-RESTful:开发REST API的工具;
- Flask JSON-RPC: 开发rpc远程服务[过程]调用
- Flask-Bootstrap:集成前端Twitter Bootstrap框架
- Flask-Moment:本地化日期和时间
- Flask-Admin:简单而可扩展的管理接口的框架
可以通过 https://pypi.org/search/?c=Framework+%3A%3A+Flask 查看更多flask官方推荐的扩展
1.2 flask的安装与创建
创建一个虚拟环境
mkvirtualenv flask -p python3 # -p:指定python3
安装
pip install flask==0.12.5
创建一个目录,并在pycharm中打开
mkdir -r /home/wangfan/flask/flask_demo01
创建一个main.py,并设置依赖环境(pycharm:file->settings)
写flask框架的主程序
#导入Flask类
from flask import Flask
# 1.创建应用对象
app = Flask(__name__)
# 2.指定路由
@app.route('/')
def index(): # 3.视图函数
return "<h1>hello,world<h1>" # 4. 返回字符串
if __name__ == '__main__':
# 5. 启动项目
app.run(debug=True, port=8001, host='0.0.0.0') # 可以指定参数,由wsgiref模块提供
代码分析:
-
创建应用对象时,传入参数的说明
-
额外加载配置的说明(1.自定义类2.通过
app.config.from_object(类名)
注册),也有其他方式如下:
# 导入Flask类
from flask import Flask
"""
import_name Flask程序所在的包(模块),传 __name__ 就可以
其可以决定 Flask 在访问静态文件时查找的路径
static_path 静态文件访问路径(不推荐使用,使用 static_url_path 代替)
static_url_path 静态文件访问路径,可以不传,默认为:/ + static_folder
static_folder 静态文件存储的文件夹,可以不传,默认为 static
template_folder 模板文件存储的文件夹,可以不传,默认为 templates
"""
app = Flask(import_name=__name__)
# 编写路由视图
# flask的路由是通过给视图添加装饰器的方式进行编写的。当然也可以分离到另一个文件中。
# flask的视图函数,flask中默认允许通过return返回html格式数据给客户端。
@app.route('/')
def index():
return "<h1>hello world</h1>"
# 加载项目配置
class Config(object):
# 开启调试模式
DEBUG = True
# flask中支持多种配置方式,通过app.config来进行加载,我们会这里常用的是配置类
app.config.from_object( Config )
# 指定服务器IP和端口
if __name__ == '__main__':
# 运行flask
app.run(host="0.0.0.0", port=5000)
2.flask之路由
2.1 路由的基本知识
1.路由: 一种访问地址[url]和应用程序[视图]进行一对一绑定的映射关系
2.往往在开发中,我们所说的路由,其实通常指代完成路由绑定关系的路由类
3.路由和视图的名称必须全局唯一,不能出现重复,否则报错。
4.可以通过参数method指定请求方法
2.2 有名分组
指定参数进行传递
from flask import Flask
app = Flask(__name__)
# 指定参数进行传递-->django路由:有名分组
@app.route('/user/<user_name>', methods=['get']) # 只能发送get请求
def index(user_name):
return "hello,%s" % (user_name)
if __name__ == '__main__':
app.run()
2.3 内置路由参数转换器
1.int:接受正整数
2.float:接受正浮点值
3.uuid:接受UUID(通用唯一识别码)字符串 xxxx-xxxx-xxxxx-xxxxx
4.string:默认类型,接受不带斜杠的任何文本
5.path:接收string
但也接受斜线
from flask import Flask
app = Flask(__name__)
# 内置路由参数转换器
@app.route('/user2/<float:user_id>')
def user(user_id):
return 'user_id:%s' % user_id
if __name__ == '__main__':
app.run()
原码:(werkzeug.routing.py)
DEFAULT_CONVERTERS = {
"default": UnicodeConverter,
"string": UnicodeConverter,
"any": AnyConverter,
"path": PathConverter,
"int": IntegerConverter,
"float": FloatConverter,
"uuid": UUIDConverter,
}
2.4 自定义路由参数转换器
也叫正则匹配路由参数.
- 引入BaseConverter路由参数转换器基类
- 自定义路由参数转换器(继承父类的构造方法)
- 注册路由参数转换器到app对象中(
app.url_map.converters['别名']=自定义类名
)
from flask import Flask
app = Flask(__name__)
# 1.引入BaseConverter路由参数转换器基类
from werkzeug.routing import BaseConverter
# 2.自定义路由参数转换器
class MobileConverter(BaseConverter):
regex = r'1[3-9]\d{9}'
def __init__(self, map, *args, **kwargs):
super().__init__(map) # map相当于django中的urlpatterns(所有路由的列表)
# 3.注册路由参数转换器到app对象中
app.url_map.converters['mob'] = MobileConverter # 起别名为mob
@app.route(rule='/user3/<mob:user_mobile>')
def user3(user_mobile):
return '%s' % user_mobile
if __name__ == '__main__':
app.run()
2.5 在路径上直接写正则匹配
相当与django中的re_path()
与2.4中不同的是:正则规则,在路径上写直接当参数传至自定义类中
两种方法的本质为:改父类的regex属性
其中,注意传参时加上引号
from flask import Flask
app = Flask(__name__)
# 1.引入BaseConverter路由参数转换器基类
from werkzeug.routing import BaseConverter
# 自定义类
class MobileConverter2(BaseConverter):
def __init__(self, map, *args, **kwargs):
self.regex = args[0]
super().__init__(map)
# 注册
app.url_map.converters['mob2'] = MobileConverter2
# 传参 (注意传参时加上引号)
@app.route(rule=r'/user4/<mob2("\d{6}"):user_id>')
def user4(user_id):
return '%s' % user_id
if __name__ == '__main__':
app.run()
2.6 路由注册和视图进行分离
通过app.add_url_rule(rule='路径',view_func=视图函数)
实现
from flask import Flask
app = Flask(__name__)
def index():
return "ok"
# 也可以让路由注册和视图进行分离
app.add_url_rule(rule="/",view_func=index)
if __name__ == '__main__':
app.run(debug=True)
3.flask之请求与响应
3.1 请求request
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求体的数据,并转换为字符串 只要是通过其他属性无法识别转换的请求体数据 最终都是保留到data属性中 |
bytes类型 |
form | 记录请求中的html表单数据 | MultiDict |
args | 记录请求中的查询字符串,也可以是query_string | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的请求头 | EnvironHeaders |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件列表 | * |
json | 记录ajax请求的json数据 | json |
ImmutableMultiDict:这个类就是一个字典的子类,我们可以称之为类字典对象,所以可以通过字典的操作来使用(思路来源:from collection import OrderedDict)
文档: http://docs.jinkan.org/docs/flask/api.html#flask.request
原码位置:from flask.app import Request
引入:from flask import Flask,request
from flask import Flask,request
app = Flask(__name__)
@app.route(rule='/', methods=['get', 'post'])
def index():
print('查询字符串', request.args)
'''
ImmutableMultiDict([
('name', 'xyw'),
('hobby', '1'),
('hobby', '2'),
('hobby', '3')
])
'''
print(request.args.get('name')) # xyw
print(request.args.getlist('hobby')) # ['1', '2', '3']
print(request.args.to_dict(flat=False)) # 转成普通字典,列表 {'name': ['xyw'], 'hobby': ['1', '2', '3']}
print(request.args.to_dict(flat=True)) # 转成普通字典,保留第一个 {'name': 'xyw', 'hobby': '1'}
print('请求体数据(bytes类型)', request.data)
print('请求中表单数据', request.form) # ImmutableMultiDict([('name', 'xyw'), ('age', '48')])
print('请求头', request.headers)
'''
请求头 Authorization: jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo2LCJ1c2VybmFtZSI6Inh5dyIsImV4cCI6MTYwNTI3NDU4MiwiZW1haWwiOiIifQ.rc0Sjezl_uAa836wUzExyNru1VEE3qtes6-sJPWCv-0
User-Agent: PostmanRuntime/7.26.5
Accept: */*
Postman-Token: b636c222-ba16-4bae-95e3-1995b7344e5f
Host: 127.0.0.1:5000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------------------------145884211271048853446909
Content-Length: 266
'''
print('请求方法', request.method)
print('请求URL', request.url)
print('上传的文件', request.files)
print('请求的json数据', request.json)
print('是否是ajax请求', request.is_json) # True
return "<h1>hello,world<h1>"
if __name__ == '__main__':
app.run()
3.2 响应
flask默认支持2种响应方式:
1.数据响应: 默认响应html文本,也可以返回 JSON格式,或其他格式
2.页面响应: 重定向
url_for 视图之间的跳转
响应的时候,flask也支持自定义http响应状态码
3.2.1 响应html文本
make_response
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/')
def index():
# return "<h1>hello,world<h1>"
return make_response("<h1>hello,world<h1>") # 与直接返回相同
if __name__ == '__main__':
app.run()
3.2.2 响应json数据
可以直接使用 jsonify 生成一个 JSON 的响应
from flask import Flask, make_response, jsonify
app = Flask(__name__)
@app.route('/')
def index():
data = {
'name': 'xyw',
'age': 18
}
return jsonify(data)
if __name__ == '__main__':
app.run()
3.2.3 重定向到外链接
redirect
from flask import Flask, make_response, jsonify, redirect
app = Flask(__name__)
@app.route('/user')
def user():
return redirect('http://www.baidu.com')
if __name__ == '__main__':
app.run()
3.2.4 重定向到其他视图
1.可以直接填写自己 url 路径
2.也可以使用 url_for 生成指定视图函数所对应的 url
from flask import url_for
from flask import Flask, make_response, jsonify, redirect, url_for
app = Flask(__name__)
@app.route('/')
def index():
data = {
'name': 'xyw',
'age': 18
}
return jsonify(data)
@app.route('/user')
def user():
return redirect(url_for("index"))
if __name__ == '__main__':
app.run()
3.2.5 重定向到其他视图时携带参数
url_for()中加上endpoint='视图名', 参数=xx
from flask import Flask, make_response, jsonify, redirect, url_for
app = Flask(__name__)
@app.route('/<userid>')
def index(userid):
return 'userid:%s' % userid
@app.route('/user')
def user():
return redirect(url_for(endpoint="index", userid=1))
if __name__ == '__main__':
app.run()
3.2.6 响应时添加响应头
添加响应头,支持文件上传
from flask import Flask
app = Flask(__name__)
from flask import make_response,jsonify,request
@app.route(rule="/user", methods=["get","post"])
def user():
# 识别身份
if request.args.get("user") == "abc":
# 也可以返回图片,压缩包 等其他在浏览器能支持的数据,既可以支持显示图片,也可以支持显示
with open("2.jpg","rb") as f:
content = f.read()
response = make_response(content)
response.headers["Content-Type"] = "image/jpeg"
return response
else:
return "没有权限"
# 支持下载
# with open("123.zip", "rb") as f:
# response.headers["Content-Type"] = "application/zip"
# return response
@app.route("/")
def index():
return "<img src='/user?user=abc'>"
if __name__ == '__main__':
app.run(debug=True)
添加自定义响应头,实现页面转跳
from flask import Flask,make_response
app = Flask(__name__)
@app.route("/user1")
def index1():
# 页面跳转
# return redirect("http://www.baidu.com") # 跳转到站外
# return redirect("/user") # 跳转到站内
# return redirect(url_for("user",user_id=100)) # 通过url_for指定视图名称,直接找到对应路由进行跳转
# 跳转的原理,实际就是利用HTML文档中的元信息
response = make_response()
response.headers["Location"] = "http://www.baidu.com" # 实现页面转跳
response.headers["Company"] = "oldboy" # 自定义响应头
response.status_code = 302 # 自定义响应状态吗
return response
if __name__ == '__main__':
app.run(port=8000)
3.2.7 Response的参数说明
- 上面make_response本质上就是Response
- Response(response=“内容”, status=“http响应状态码”,headers=自定义响应头,mimetype=“数据格式”)
from flask import Flask,request,make_response,Response,jsonify
app = Flask(__name__)
@app.route("/")
def index():
"""返回html数据"""
# return "ok"
# return make_response("ok") # 上面的代码是这段代码的简写
# return Response("ok") # 上面make_response本质上就是Response
"""返回json格式数据"""
# data = {"name":"xiaoming","age":13}
# return jsonify(data)
"""返回其他类型数据"""
# 关于Response常用的参数
# Response(response="内容", status="http响应状态码",headers=自定义响应头,mimetype="数据格式")
# return Response(response="ok",status=201,headers={"company":"hello"})
# 返回图片信息
with open('./1.zip',"rb") as f:
content=f.read()
# 判断权限,身份...
return Response(response=content,mimetype="application/zip")
if __name__ == '__main__':
app.run(debug=True)
4.cooike和session
4.1 cookie
4.1.1 cookie基本知识回顾
产生原因:
http协议(基于tcp)的特点:1 无连接(短连接);2 无状态.
http协议不会记录客户端和服务端的任何信息,导致服务端和客户端不能维持会话
正是由于http无状态的特性,所以出现了cookie(一种浏览器技术).请求头健值对.
工作原理:
浏览器访问服务端,带着一个空的cookie,然后由服务器产生内容,浏览器收到相应后保存在本地;
当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。
4.1.2 操作cookie
from flask import Flask, request, make_response, session
app = Flask(__name__)
@app.route('/set_cookie')
def set_cookie():
# 设置cookie
response = make_response('ok')
response.set_cookie('username', 'xyw', 30) # key=键,value=值,max_age=有效时间(秒)
response.set_cookie('age', '108') # 如果cookie没有设置过期时间,则默认过期为会话结束过期
# 会话结束:浏览器关闭;意味着下一个站点下同变量名的cookie会被覆盖
return response
@app.route('/get_cookie')
def get_cookie():
# 获取cookie
print(request.cookies)
print(request.cookies.get('username'))
return 'ok'
@app.route('/del_cookie')
def del_cookie():
# 删除cookie
response = make_response('ok')
response.set_cookie('age', '', 0) # 设置有效时间覆盖原有的cookie达到删除的目的
return response
if __name__ == '__main__':
app.run(port=8002)
4.2 session
4.2.1 session知识回顾
产生原因:
1.cookie是明文存储的
2.大小限制:
Cookie大小上限为4KB;
一个服务器最多在客户端浏览器上保存20个Cookie;
一个浏览器最多保存300个Cookie,因为一个浏览器可以访问多个服务器。
特点:
cookie中放的数据是密文的
数据存在服务端,没有大小上限
一个浏览器对应一个服务端,就是一个session
4.2.2 操作session
from flask import Flask, request, make_response, session
app = Flask(__name__)
# 需要配置一个secret_key用于session加密
class Config():
SECRET_KEY = '123456'
app.config.from_object(Config)
@app.route('/set_session')
def set_session():
# 设置session
# session 存在服务器的缓存中,支持python基本数据类型作为值
session['username'] = 'xyw'
session['info'] = {
'age': 11,
'sex': False
}
'''
浏览器中cookies中:
session:eyJpbmZvIjp7ImFnZSI6MTEsInNleCI6ZmFsc2V9LCJ1c2VybmFtZSI6Inh5dyJ9.X7YptA.J4GTqRRgsnt2ijZJLIhV5X-3pq8
'''
return 'ok'
@app.route('/get_session')
def get_session():
# 获取session
ret1 = session.get('username')
ret2 = session.get('info')
return '%s+%s' % (ret1, ret2)
@app.route('/del_session')
def del_session():
# 删除session
try:
del session['username']
# session.clear() # 删除所有
except:
pass
'''
删除了之后在获取得到:
None+{'age': 11, 'sex': False}
'''
return 'ok'
if __name__ == '__main__':
app.run(port=8002)
5.视图请求钩子
5.1 请求钩子作用
在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:
- 在请求开始时,建立数据库连接;
- 在请求开始时,根据需求进行权限校验;
- 在请求结束时,指定数据的交互格式;
为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设置的功能,即请求钩子。
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:
- before_first_request
- 在处理第一个请求前执行[项目初始化时的钩子]
- before_request
- 在每次请求前执行
- 如果在某修饰的函数中返回了一个响应,视图函数将不再被调用
- after_request
- 如果没有抛出错误,在每次请求后执行
- 接受一个参数:视图函数作出的响应
- 在此函数中可以对响应值在返回之前做最后一步修改处理
- 需要将参数中的响应在此参数中进行返回
- teardown_request:
- 在每次请求后执行
- 接受一个参数:错误信息,如果有相关错误抛出
- 需要设置flask的配置DEBUG=False,teardown_request才会接受到异常对象。
5.2 实例
from flask import Flask
app = Flask(__name__)
@app.before_first_request
def first_request():
print('1.项目启动以后,首次请求后执行')
@app.before_request
def before_request():
print('2.每次请求时,执行')
@app.after_request
def after_request(response):
# 会将响应结果传递进来
print('4.视图执行之后,执行')
return response
@app.teardown_request
def have_error(exc):
# 用来接收异常
print('5.在after_request执行后,执行')
'''
1. 在debug=False时,才能接收错误被执行,在debug=True时,只执行到有错误的那一行
2. 没有异常时,在after_request执行后,执行
3. 在有异常时,在有异常的那一行执行后,立即执行,(视图函数,after_request不在执行)
'''
print(exc)
@app.route('/')
def index():
1 / 0
print('3.视图函数执行了')
return 'ok'
if __name__=='__main__':
app.run(debug=False, port=8000)
代码执行效果:
有异常时:
6.执行上下文
执行上下文:
1.请求上下文 : request;session(来自于客户端)
2.应用上下文 : current_app;g(flask 应用程序运行过程中,保存的一些配置信息)
两者区别:
请求上下文:保存了客户端和服务器交互的数据,一般来自于客户端。
应用上下文:flask 应用程序运行过程中,保存的一些配置信息,比如路由列表,程序名、数据库连接、应用信息等
request:封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get(‘user’),获取的是get请求的参数。
session:用来记录请求会话中的信息,针对的是用户信息。举例:session[‘name’] = user.id,可以记录用户信息。还可以通过session.get(‘name’)获取用户信息。
from flask import Flask, current_app, g
'''
执行上下文:
1.请求上下文 : request;session(来自于客户端)
2.应用上下文 : current_app;g(flask 应用程序运行过程中,保存的一些配置信息)
'''
app = Flask(__name__)
@app.route('/')
def index():
print(current_app.config) # 获取当前项目的所有配置信息
print(current_app.url_map) # 获取当前项目的所有路由信息
func()
print(g.name)
return 'ok'
@app.before_request
def before_request():
g.name = 'xyw'
7.Flask-Script
文档: https://flask-script.readthedocs.io/en/latest/
这个模块的作用可以让我们通过终端来控制flask项目的运行,类似于django的manage.py
安装:
pip install flask-script
7.1 manager 自带两个命令:runserver/shell
from flask import Flask
from flask_script import Manager, Command, Option
app = Flask(__name__)
# 使用flask_script启动项目
manage = Manager(app)
if __name__ == '__main__':
'''
manager 自带两个命令:runserver/shell
端口和域名不写,默认为127.0.0.1:5000
python 04Flask-Script.py runserver
通过-h设置启动域名,-p设置启动端口
python 04Flask-Script.py runserver -h127.0.0.1 -p8888
'''
manage.run()
7.2 自定义命令
1.定义一个类 继承Command
2.添加参数option_list
3.添加run方法
4.将自定义的类加到终端脚本工具中(add_command)
from flask import Flask
from flask_script import Manager, Command, Option
app = Flask(__name__)
@app.route('/')
def index():
return 'ok'
# 1.定义一个类 继承Command
class HelloCommand(Command):
"""a simple example"""
option_list = [ # 2.添加参数
Option('--name', '-n', help='名称'),
Option('--num', '-m', help='数量')
]
# 3.添加run方法
def run(self, name, num): # 输入命令时,执行
print('name=%s' % name)
print(num)
print('hello,world')
# 使用flask_script启动项目
manage = Manager(app)
# 4.将自定义的类加到终端脚本工具中(add_command)
manage.add_command('hello', HelloCommand)
if __name__ == '__main__':
manage.run()
7.3 用自定义命令生成项目目录
from flask import Flask
from flask_script import Manager, Command, Option
app = Flask(__name__)
@app.route('/')
def index():
return 'ok'
# 使用命令生成目录
import os
class BluePrintCommand(Command):
option_list = [
Option('--name', '-n', help='蓝图名称')
]
def run(self, name=None):
if name is None:
print('蓝图名称不能为空')
return
if not os.path.isdir(name):
os.mkdir(name)
open('%s/views.py' % name, 'w')
open('%s/models.py' % name, 'w')
with open('%s/urls.py' % name, 'w') as f :
f.write('''from . import views
urlpatterns = [
]
''')
manage.add_command('bule', BluePrintCommand)
if __name__ == '__main__':
manage.run()
8.Jinja2模板引擎
使用Flask提供的render_template
函数,该函数封装了该模板引擎
它的第一个参数为页面的文件名,后面的参数为模板中变量对应的真实值(键值对)
8.1 模板的基本使用
1.创建应用对象的时候,添加template_folder参数(存放页面的目录位置)
2.使用Flask提供的render_template函数
main.py:
from flask import Flask, render_template
# 1.创建应用对象的时候,添加template_folder参数(存放页面的目录位置)
app = Flask(__name__,
template_folder='templates', # html页面
static_folder='static', # 静态资源
static_url_path='/static' # 提供给外界的访问路径
)
# 基本使用
@app.route('/')
def index():
data = {
}
data['title'] = 'hello,world'
data['num'] = 100
#2.使用Flask提供的render_template函数
return render_template('index1.html', **data)
if __name__ == '__main__':
app.run(port=8000)
在项目目录下创建static
目录,index1.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{
{title}}</title>
</head>
<body>
<h1>{
{num}}</h1>
<a href='/show_info'>信息展示</a>
<a href='/show_contain'>内置变量展示</a>
<a href='/filter'>过滤器</a>
<a href='/jicheng'>继承</a>
</body>
</html>
8.2 各种数据的模板渲染及if语句和for语句的使用
Jinja2 模版中的变量代码块可以是任意 Python 类型或者对象,只要它能够被 Python 的 __str__
方法或者str()转换为一个字符串就可以。
main.py:
from flask import Flask, render_template, session, g
app = Flask(__name__,
template_folder='templates', # html页面
static_folder='static', # 静态资源
static_url_path='/static' # 提供给外界的访问路径
)
# 各种数据类型的模板渲染
@app.route('/show_info')
def show_info():
data = {
}
data['info'] = {
'name': 'xyw',
'age': 18,
'sex': True,
}
data['student_list'] = ['xyw1', 'xyw2', 'xyw3', 'xyw4', 'xyw5']
data['goods_list'] = [
{
"id": 10, "name": "Python7天入门到放弃", "price": 99.9, "num": 100},
{
"id"