使用 pip install flask
进行安装,使用 from flask import Flask
引入
flask 能够很简单的根据 http 请求输出数据。因为 flask 是一个 web 的应用框架,所以使用时需要创建和运行相应的应用。
from flask import Flask
# 创建 web 应用程序
app = Flask(__name__) # 参数是模块名称,可以随便写,一般小写字母,表示 flask 应用对象名称
if __name__ == '__main__':
app.run(debug=True) # 启动应用程序(flask项目) (debug=True 可以在更改代码后不用重启应用,也可以不写)
这时运行程序,可以看见运行窗口提示 Running on http://127.0.0.1:5000 (Press CTRL+C to quit) 就可以使用这个地址在浏览器里访问 web 程序了。
可以在初始化 app 对象时添加参数,进行一些设置。
也可以在 run()
函数中添加一些参数,用来设置 web 服务器,例如访问 IP 和端口。
app.run(host='127.0.0.1', port=5000)
还有一些默认的参数可以修改
需注意的是,多进程和多线程不能同时开启。
在创建了应用对象后,运行之前,可以对 app 进行配置
# 使用 app.config 来添加设置,例如密钥
app.config['SECRET_KEY'] = os.urandom(24) # 设置随机的24位字符串作为密钥
或者使用配置文件 settings.py
# settings.py
class Dev():
ENV = 'developement'
SECRET_KEY = 'ddkakda%&akd#@'
STATIC_URL_PATH = '/s'
class Product():
ENV = 'production'
SECRET_KEY = 'drtjuda%&akd#@'
# app.py
from flask import Flask
import settings
app = Flask(__name__)
# 从指定的对象中加载 Flask 服务的配置
app.config.from_object(settings.Dev)
路由就是访问路径,即访问地址中,域名、端口之后的部分。每个路由请求由一个处理函数进行处理。
from flask import Flask
# 创建 web 应用程序
app = Flask(__name__)
# 处理函数和路由
@app.route('/') # 路由
def index(): # 路由的处理函数,函数名称没有要求,可以随便起
return '访问主页测试'
if __name__ == '__main__':
app.run(debug=True) # 启动应用程序(flask项目) (debug=True 可以在更改代码后不用重启应用,也可以不写)
可以使用 app.add_url_rule ( '/ ' , view_func = viewFunction )
来注册路由。参数1是路径,参数2是视图函数
from flask import Flask
app = Flask(__name__)
def fn():
return "lxc"
app.add_url_rule('/',view_func=fn)
if __name__ == '__main__':
app.run(debug=True)
在基于类的视图中 —— 即插视图,必须使用这种方式来注册路由。其实使用装饰器来注册路由的底层是使用 add_url_rule()
方法来注册的 ,只不过 flask 把 add_url_rule()
做了一层封装,封装成了一个装饰器,使用起来更加方便优雅。
这里举两个规则
@app.route(‘/projects/’)
@app.route(‘/about’)
这两个 url 规则看起来很相似,但是结尾斜线的使用在 URL 定义中不同。第一种情况时,访问一个结尾不带斜线的 URL 会被 flask 重定向到带斜线的规范 URL 去。而第二种情况下,访问结尾带斜线的 URL 则会产生一个 404 错误。
有时候,我们需要跳转到一个页面,但是页面地址可能不太确定(例如添加了前缀、使用路径参数等)。这时候我们可以使用反向解析(url_for)的方法,通过视图处理函数获取反向路径。
url_for(‘函数名’, 参数名=值)
url_for(‘蓝图名.函数名’, 参数名=值)
from flask import url_for
@app.route('/ok/' )
def ok(para):
return '跳转成功,获取参数 %s' % para
@app.route('/index')
def index():
para = '参数值'
return "跳转链接" % url_for('ok', para=para)
from flask import url_for
from flask import Blueprint
blue = Blueprint('test', __name__)
@blue.route('/ok/' )
def ok(para):
return '跳转成功,获取参数 %s' % para
@blue.route('/index')
def index():
para = '参数值'
return "跳转链接" % url_for('test.ok', para=para)
需注意 url_for()
函数的参数1必须是字符串类型。
可以在路由信息参数中添加 methods 来确定处理的请求类型,如果没有此参数,则默认处理 get 请求。methods 是一个列表,可以添加多个类型。
from flask import Flask
# 创建 web 应用程序
app = Flask(__name__) # 参数是模块名称,可以随便写
# 处理函数和路由
# 支持 RESTful 风格中关于资源的动作。
@app.route('/', methods=['GET', 'POST']) # 路由,参数2是请求类型。也支持 PUT、DELETE、PATCH 等其他类型
def index(): # 路由的处理函数,函数名称没有要求,可以随便起
return '访问主页测试'
if __name__ == '__main__':
app.run(debug=True) # 启动应用程序(flask项目) (debug=True 可以在更改代码后不用重启应用,也可以不写)
Flask 使用的是 MTV 设计思想,是基于 MVC 的:
所以除了名称不同,设计思想基本是一致的。
对于 flask 来说,没有必要的必须遵守的目录结构。即使需要返回的模板文件(HTML文件)也可以在定义 app 时,使用参数更改其目录。但是做项目时还是推荐使用规范的目录结构,方便资源的调用和修改。
app 所在目录中,使用 view.py(或创建 views 文件夹,使用此文件夹里创建的 py 文件) 来定义视图处理函数,使用 models.py 来定义模型,使用 templates 来存放模板文件,使用 static 来存放静态资源。
对于附带参数或数据的 http 请求,需要获取其中的数据内容,使用 flask 库的 request 对象(HttpRequest)。request 包含了请求资源的路径、请求方法、请求头、上传的表单数据、文件等信息。
通过 request.method
可以获取请求方法
if request.method == 'GET':
pass
elif request.method == 'POST':
pass
使用 request.url
获取完整的路径,包含根路径
#请求 http://0.0.0.0:9527/req?id=10
print(request.url) # 'http://0.0.0.0:9527/req?id=10'
使用 request.base_url
获取完整路径,包含根路径,但不包含get参数
#请求 http://0.0.0.0:9527/req?id=10
print(request.base_url) # 'http://0.0.0.0:9527/req'
使用 request.path
可以获取获取斜线后边的url路径 (不包含根路径)
# 请求的是 http://0.0.0.0:9527/req?id=10
print(request.path) # '/req'
使用 request.host
获取根路径
# 请求路径为 http://0.0.0.0:9527/req?id=10
print(request.host) # http://0.0.0.0:9527
使用 request.host_url
获取根路径,包含后边斜线
# 请求路径为 http://0.0.0.0:9527/req?id=10
print(request.host) # http://0.0.0.0:9527/
使用 request.headers
获取请求头信息
使用 request.cookies
获取 cookie 数据
使用 request.remote_addr
获取访问的远端ip地址
使用 request.files
获取上传的文件
get 请求的数据是添加在 url 内的,可以使用 reuqest.args
来获取参数。可以使用 get()
方法获取特定的数据(使用此方法好处是如果没有返回 None,而其他的方法会出错),也可以使用 to_dict()
将 form 数据转换为字典。
from flask import Flask, request
# 请求地址:http://127.0.0.1:5000/data?u=1112&p=3324
@app.route('/data')
def api_data():
# 获取 url 传参
u = request.args.get('u', 'args没有参数 u') # 获取参数 u
p = request.args.get('p', 'args没有参数 p') # 获取参数 p
q = request.args.get('q', 'args没有参数 q') # 获取参数 q
return u + p + q
if __name__ == '__main__':
app.run(debug=True) # 启动应用程序(flask项目) (debug=True 可以在更改代码后不用重启应用)
将参数添加到 URL 里,例如 http://web.io/find/112
,其中 112 就可以是参数。
# 使用转换器 converter 来转换参数,语法格式为 ,例如
@app.route('/find/' , methods=['GET'])
def find(word):
# 将 int 型的参数以 word 变量传入
pass
converter 是参数转换器,一般是指定类型,如 int, float, path。 path 转换器主要用于引用别的网址,例如
@app.route('/forward/' )
def forward(url):
# 重定向到 url
return redirect(url)
以上配置的路由,对于路径 "/forward/http://www.baidu.com"
是合法的,如果将 path 转换器换成 string 则可能会报错。
另外如果需要使用到 string 类型的参数,则省略 converter 部分
@app.route('/user/' )
def show_user_profile(username):
return '用户 %s' % username
如果使用多个参数,则需要使用斜杠隔开
@app.route('/user//' )
def show_user_profile(username, id):
pass
注:路径中的变量名要和视图处理函数的参数名相同。
使用 post 提交的数据一般是表单数据。也有其他类型数据,例如 JSON 或二进制文件等。
使用 request.form
来获取数据。可以使用 get()
方法获取特定的数据(使用此方法好处是如果没有返回 None,而其他的方法会出错),也可以使用 to_dict()
将 form 数据转换为字典。
from flask import Flask, request
@app.route('/login', methods=['post'])
def login():
# 从请求中获取数据
username = request.form.get('username', '') # 获取 form 中的数据
password = request.form.get('pwd', '') # 获取 form 中的数据
if username == '123123' and password == '123':
return '成功'
if __name__ == '__main__':
app.run(debug=True) # 启动应用程序(flask项目) (debug=True 可以在更改代码后不用重启应用)
注,用来获取请求头为 Content-Type : application/x-www-form-urlencoded
的数据,如果是 JSON 数据是获取不到的
可以使用 request.get_json()
或 request.json()
获取 json 数据,两个方法是等价的。
@app.route('/home',methods=["GET","POST"])
def fn():
if request.method == "POST":
data = request.get_json()
print(data)
return "获取了JSON数据"
注,用来获取请求头为 Content-Type : application/json
的数据,如果是 form 数据是获取不到的,会是 None,除非设置参数 force get_json(force=True)
,此时会忽略mimetype并始终尝试解析json。
mimetype说穿了其实指的就是文件后缀名。你向web服务器请求一个文件,服务器会根据你的后缀名去匹配对应的值设置为response中content-type的值。而content-type是正文媒体类型,游览器根据content-type的不同来分别处理你返回的东西。
根据 http 请求做出响应,返回需要的内容。所有返回前台的内容其实都应该是 Response 的对象或者其子类,我们看到如果返回的是字符串直接可以写成 return '字符串'
的形式,但是其实这个字符串也是经过了Response包装的:return Response('字符串')
flask 可以在函数 return 中添加任意数据,如果返回一个 json ,就成了一个简单的 WebAPI 。
from flask import Flask
# 创建 web 应用程序
app = Flask(__name__) # 参数是模块名称,可以随便写
# 处理函数和路由
@app.route('/', methods=['post', 'get']) # 路由
def index(): # 路由的处理函数,函数名称没有要求,可以随便起
return '访问主页测试'
# 简单的 api
@app.route('/json')
def api_json():
from flask import jsonify
j = {'name': 'Timmy', 'age': '18'}
j = jsonify(j) # 将字典序列化为 json 字符串
return j
if __name__ == '__main__':
app.run(debug=True) # 启动应用程序(flask项目) (debug=True 可以在更改代码后不用重启应用,也可以不写)
这里需要注意的是,json 模块的 json.dumps()
方法也能将数据序列化为 json 字符串,但是在查看头后会发现响应内容为 text/html
,而 flask 模块的 jsonify
方法响应内容类型为 application/json
。相当于 jsonify
方法返回一个响应对象,内容是使用 json.dumps
方法序列化的 json 字符串,并且将响应头的 Content-Type
设置为了 application/json;charset=utf-8
。
需要导入abort模块,终止响应的意思,通常会在参数中写403或404等错误代码
from flask import abort
@app.route('/list')
def fn():
d = jsonify({'name':'guest','age':20})
l = jsonify([1,2,3])
t = jsonify(('1,2,3','abc'))
abort(403)
return t
'''
当访问该路径时,会报错
You don't have the permission to access the requested resource. It is either read-protected or not readable by the server.
意思是:您没有访问所请求资源的权限。它要么是受读保护的,要么是服务器不可读的。
或在 abort() 方法内部传入一个 Response 对象。
abort(Response("当前访问被拒绝,原因:xxxxxx
", 403))
'''
可以使用钩子函数定义一些错误处理函数,可以通过状态码处理相应的错误。
@app.errorhandler(404)
def notfounded(error):
return render_template('404.html')
@app.errorhandler(403)
def Forbidden403(error):
return redirect('/403')
也可以使用 Exception 对象来获取错误,然后再判断。使用 Exception 除了正常的访问错误外,还可以处理 raise Exception()
抛出的异常。raise 抛出的异常状态码为 500 (但不可以通过状态码来处理这个异常)。
@app.errorhandler(Exception)
def error(err):
# err 就是传入的错误,是 werkzeug.exceptions 下的错误类型
# 可以直接输出为错误信息,也可以通过判断 err 的数据类型来做响应处理
if type(err) == werkzeug.exceptions.Forbidden:
return render_template('403.html')
elif type(err) == werkzeug.exceptions.NotFound:
return redirect('/404')
else:
return err
当状态码和 Exception 混用时,会优先使用状态码处理错误,没有相应状态码处理错误函数时,使用 Exception 处理其余的错误。
需要注意的是,错误处理函数必须有一个参数,用来获取错误信息。如果没有这个参数则会报错。
可以使用 flask 的 make_response()
方法来自定义响应对象(也可以使用 flask 的 Response 类)
from flask import make_response
@app.route('/index')
def index():
data = {'id':101,'age':20} # 数据
code = 200 # 状态码
response = make_response(data,code) # 将数据和状态码封装进响应对象
# 或使用 response = make_response() 创建空的 response 对象
# 然后使用 response.data = data 设置响应体
# 使用 response.status_code = code 设置响应状态码
# 设置响应头
response.header['Content-Type'] = 'application/json;charset=utf-8'
# response = Response(data, code, content_type='application/json')
return response
数据可以直接返回,如果需要返回一个 html 页面,则需要使用 flask 库内的 render_template 。
使用时,需要在 app 所在文件同一个目录下新建目录 templates (名称不能出错),然后将需要呈现的 html 文件放在这个目录下。
from flask import Flask, render_template
# 创建 web 应用程序
app = Flask(__name__) # 参数是模块名称,可以随便写
@app.route('/login')
def login():
return render_template('login.html') # 访问 html 页面
if __name__ == '__main__':
app.run(debug=True) # 启动应用程序(flask项目) (debug=True 可以在更改代码后不用重启应用,也可以不写)
from flask import redirect
@app.route('/login')
def login():
return redirect('/index') # 跳转的可以是相对路径,也可以是绝对路径(不带根路径)
# 通常会结合 url_for() 使用
将 python 中的变量发给 html 页面中的变量, html 中使用双大括号将变量括起来。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>flask 学习测试title>
head>
<body>
这是一个 flask 学习的测试页面,传进来的值是 "{{value}}"
<hr />
{% for item in lst %}
这个是列表数据:{{item}} <br />
{% endfor %}
body>
html>
from flask import Flask, render_template
# 创建 web 应用程序
app = Flask(__name__) # 参数是模块名称,可以随便写
@app.route('/') # 路由函数,当访问到 '/' 即根目录时执行函数
def index():
# 把数据发送到页面
s = '你好,这个是测试 flask 给页面传值' # 字符串
lst = ['0001', '0002', '0003', '0004', '0005']
# 返回模板 html
return render_template('hello.html', value=s, lst=lst) # 传值进模板页面,将变量 s 传给页面中的变量 value
"""
也可以将数据以字典形式发送回页面
data = {
'value' : '你好,这个是测试 flask 给页面传值',
'lst' : ['0001', '0002', '0003', '0004', '0005']
}
return reder_template('hello.html', **data) # 发送字典其实是发送字典的内容项,所以使用 **
需注意发送字典时,字典的 key 需要与前端使用的变量名相对应。
"""
if __name__ == '__main__':
app.run(debug=True) # 启动应用程序(flask项目) (debug=True 可以在更改代码后不用重启应用,也可以不写)
也可以使用 locals()
方法,收集当前函数内部的变量,生成字典对象,再返回给前端页面。
@app.route('/')
def index():
# 把数据发送到页面
value = '你好,这个是测试 flask 给页面传值' # 字符串
lst = ['0001', '0002', '0003', '0004', '0005']
# 返回模板 html
return render_template('hello.html', locals())
需注意的是,locals()
方法会收集当前函数之前所有定义的变量,有些是不能被序列化返回页面的。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>flask 的传值测试页面title>
head>
<body>
<form action="/login" method="post">
用户名 <input type="text" name="username" >
密码 <input type="password" name="pwd">
<input type="submit" value="登录"><br/>
{{res}}
form>
body>
html>
@app.route('/login', methods=['post', 'get'])
def log():
# 从请求中获取数据
username = request.form.get('username')
password = request.form.get('pwd')
if not username and not password:
return render_template('login.html')
elif username == '123123' and password == '123':
return render_template('login.html', res='登录成功')
else:
return render_template('login.html', res='登录失败')
Flask 能够在静态的网页文件中,将一些预先标记的数据进行渲染处理,然后输出。这些静态网页文件就成为了动态的模板文件,这些标记使用的就是模板语言。flask 识别模板语言后,根据内容,进行控制、填充等操作,填入合适内容到网页文件中然后渲染输出。
模板语言除了可以用来获取数据外,也可以用来执行判断、循环等操作。flask 使用的是 jinja 模板技术,使用了 Mustache 语法,此语法很多地方都在使用,例如 Vue、Django 等。
flask 的模板语法都是包含在 {}
中的。
在模板语言中,不支持方括号式获取索引下标、元素等,而是使用 “.” 。这个点使用在很多地方获取数据
{# 注释内容 #}
,注释内容不会被渲染。
使用双花括号可以接收数据或使用过滤器
使用 render_template()
方法时,将数据发送给页面相应的变量:
def index(request):
text = '初学 Django'
return render_template('index.html', n1=text)
在页面中使用 {{ 变量名 }} 来接送 render_template()
方法传递的数据。
<h1> {{ n1 }} h1>
过滤器,就是将传入的数据以某些方法进行过滤或修改。使用管道符分隔数据和方法,冒号添加方法的参数(部分参数也可以写到括号内,类似于调用 python 的相应方法传参),可以类似链式调用的方式多次过滤。其格式是
{{ 数据变量 | 过滤方法1:参数 | 过滤方法2:参数 | 过滤方法3:参数 }}
例如:
{{ 日期数据 | date:“Y-m-d H:i:s” }}
使用方法上大部分都类似于 python 的相应方法。
常用的过滤器有
capitalize : 整个字符串首字母大写
lower : 全小写
upper : 全大写
title : 字符串中每个单词首字母大写
trim : 去除前后空格
reverse : 逆序排列(反转)
format : 格式化字符串
tojson : 转换为 json 对象
striptags : 渲染之前将值中的标签去掉(如果不去掉则直接以文本显示)
safe : 确定值中的标签是安全的,可以渲染出来
default : 如果值未定义,则返回默认值
last : 返回序列的最后一项
first : 返回序列的第一项
sum : 返回序列数字之和
sort : 排序,类似于 python 的 sort 方法
unique : 序列中去重,以迭代器方式返回,通常和 join 一起使用
join : 类似 python 的 join 方法,使用指定连字符连接可迭代对象中每一个元素,返回字符串
list : 将生成器、可迭代对象转为列表
也可以自定义过滤器,例如写一个格式化日期的过滤器
@app.template_filter('datefmt') # 定义一个名称为 datefmt 的过滤器
def datefmt(value, *args): # value 是获取的值,args 是获取的参数
if type(value) == datetime:
return value.strftime(args[0])
else:
from datetime imoprt datetime
return datetime.strptime(value, args[0])
列表数据的传递和单个数据是一样的。注意下标使用 . 而不是方括号
def index(request):
text=["Python", "C", "Java", "Basic"]
return render_template('index.html', n1=text)
<h1>计算机编程语言有 {{ n1.0 }},{{ n1.1 }},{{ n1.2 }},{{ n1.3 }} 等h1>
字典数据的传递是一样的。接收时,使用 字典名.key
来获取字典数据。
def index(request):
text ={"y1": "Python", "y2": "C", "y3": "Java", "y4": "Basic"}
return render_template('index.html', n1=text)
<h1>第一种语言是 {{ n1.y1 }},第二种语言是 {{ n1.y2 }},第三种语言是 {{ n1.y3 }},第四种语言是 {{ n1.y4 }}h1>
{% %} 这个方法主要用在结构标签、宏定义和循环及流程控制中。
结构标签主要包含 block、extends和include
block 和 extends 主要用于母版。有些页面内容可以作为母版使用,在母版页面中填充子页面,可以提高代码复用,减少工作量。
在母版页中,需要更改内容(放置子页面)的地方,使用 block 进行标记,即制作好了母版页。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>部门列表title>
<link rel="stylesheet" href="/plugins/bootstrap-3.3.7-dist/css/bootstrap.min.css">
head>
<body>
<script src="/js/jquery-3.6.0.min.js">script>
<script src="/plugins/bootstrap-3.3.7-dist/js/bootstrap.min.js">script>
{# 导航条 #}
<nav class="navbar navbar-default">
...
nav>
{# 内容 #}
<div>
{% block content %}{% endblock %}
div>
body>
html>
在子页面的第一行,写入要继承的母版页,然后将需要填充到母版页的代码同样使用 block 标记即可。
{% extends 'layout.html' %}
{% block content %}
{#内容#}
<div class="container">
<div class="panel panel-default">
...
div>
div>
{% endblock %}
需注意的是,使用相同的 block 标记(即 block 后的名称相同,通常用在子页面被其它子页面继承的情况),后者会覆盖前者。如果不想覆盖, 需要后者在 block 块内部添加 {{ super() }} 继承前者。
在当前页面中,导入目标页面内容,则使用 {% include '页面文件’ %}
。这种方法将导入页面的 head 标签和 body 标签去掉(但是保留其内容),然后再进行整体导入。此方法可能会因 id 冲突、脚本冲突等原因引起页面混乱,所以不推荐使用。
宏定义使用 macro,可以在模板中定义、调用函数,来生成需要的 HTML 内容。
定义宏时需添加名称,然后在内部添加内容。
{% macro macro_name() %}
<ul>
<li>列表1li>
<li>列表2li>
<li>列表3li>
ul>
{% endmacro %}
调用宏时,直接在需要添加宏内容的地方使用 {{ macro_name() }}
即可。调用外部文件中定义的宏需要导入,使用 {% from 宏定义文件 import 宏名称 %}
,注意宏名称不带括号。
使用循环语句进行循环控制,使用判断语句进行流程控制
循环的语法如下:
{% for item in n1 %}
<span>{{ item }}span>
{% endfor %}
字典也可以使用循环
{% for k, v in n1.items() %}
<li>{{ k }} = {{ v }} li>
{% endfor %}
还可以使用 loop 对象来获取一些循环的数据信息,例如
{% for item in n1 %}
<span>{{ loop.index }} - {{ item }}span>
{% endfor %}
loop 的一些常用的方法有
loop.index : 从1开始循环
loop.index0 : 从0开始循环
loop.first : 是否为循环的第一个元素
loop.last : 是否为循环最后一个元素
loop.revindex : 从1开始逆序循环
loop.revindex0 : 从0开始逆序循环
loop.length : 循环序列元素的个数
判断语句语法如下:
{% if text == "Flask老手" %}
<h1> 高手高手高高手 h1>
{% elif text == "初学 Flask" %}
<h1> 菜鸟一个 h1>
{% else %}
<h1> 无法识别 h1>
{% endif %}
把敏感数据经过加密后放入session
中,然后再把 session
存放到 cookie
中,下次请求的时候,再从浏览器发送过来的 cookie
中读取 sesson
,然后再从 session
中读取敏感数据,并进行解密,获取最终的数据。flask的这种 session
机制,可以节省服务器开销,因为把所有的信息存到了客户端(浏览器)。
在flask中,session是通过 from flask import session
引入的。使用时添加key和value进去即可。并且,flask中的session机制,是将session信息加密,然后存储在cookie中。
import os
from flask import session
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24) # 设置随机的24位字符串作为密钥
# 或使用 app.secret_key = '1234567890' 来设置
@app.route('/')
def fn():
session['username'] = 'guest' # 设置 session
return 'success'
因为 session 需要经过加密存储在 cookie 中,所以需要设置密钥(SECRET_KEY),且这个值必须是24位的字符串。使用随机密钥会产生个问题,及每次服务重启后密钥会重置,所以重启前的 session 会丢失(因为密钥不一致了)。
@app.route('/get')
def fn1():
session_info = session.get('username')
print(session_info) # 'lxc'
return 'success'
删除session中的值,相当于调用字典方法
@app.route('/delete')
def dele():
del session['username'] # 方法一:删除session中指定的值
session.pop('username') # 方法二;弹出session中指定的值
session.clear() # 方法三:删除session中全部的值
return '删除成功'
需要引入 datetime 模块
from flask import Flask,session
from datetime import timedelta
app = Flask(__name__)
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=2) # 配置超时时间
@app.route('/add')
def fn():
session['username'] = '访客'
session.permanent = True # 启用超时
return 'guest'
除了 session 自动加密并写入 cookie,还可以手动写入 cookie 数据。
@app.route('/login')
def login():
response = make_response('进入登录页面')
# 添加 cookie
response.set_cookie('username', 'guest', max_age=600)
return response
set_cookie()
方法有以下常用参数
当有 cookie 时,访问相应页面(cookie 的 path 和 domain 指定的页面)时,request 会附带上 cookie 信息,此时就能够获取 cookie 数据了
@app.route('login')
def login():
user = request.cookies.get('username','')
pwd = request.cookies.get('password','')
pass
cookie 信息除了超时外,还可以手动删除
@app.route('/login')
def login():
response = make_response('进入登录页面')
# 删除现有信息
response.delete_cookie('username')
return response
g 其实就是 global,它专门用来保存用户数据,g对象在一次请求中,当前项目所有文件中都可以使用到;但是第二次请求时,g对象会被重新创建。
使用g对象需要先引入该模块
from flask import g
然后就可以使用了
g.key = value
例如
from flask import Flask,g,render_template,request
from readlog import read # 引入打印用户信息模块(文件)
from datetime import datetime
# 渲染页面
@app.route('/')
def fn():
return render_template('html.html')
#登录,把用户信息存到 g 对象
@app.route('/login',methods=['GET','POST'])
def login():
if request.method == 'POST':
g.username = request.form.get('username')
g.password = request.form.get('password')
read()
return '登陆成功'
def read():
now = datetime.now()
print('登陆时间是:%s' % str(now)) # 登陆时间是:2019-09-05 15:08:32.457721
print('用户账号是:%s,密码是:%s ' % (g.username,g.password))
if __name__ == '__main__':
app.run(debug=True)
flask 可以使用一些插件来扩展功能
flask-script 能够批量管理多个app,并且使用脚本的方式运行它们
使用 flask-script 需要先安装插件包
pip install flask-script
然后在 app 文件夹同级创建一个服务文件,server.py (名称可以更改)。在 app 文件夹里可以创建应用,例如将 app 文件夹设置为包文件,然后在 __init__.py 里创建 app
# app/__init__.py
from flask import Flask
app = Flask(__name__)
# 也可以在这里对 app 进行设置
然后在 server.py 引用 app,然后创建 manager 对象。
# server.py
from app import app
from flask_script import Manager
if __name__ == '__main__':
# 以脚本方式启动 flask 应用服务
manager = Manager(app)
manager.run()
之后就可以使用脚本命令来运行了(类似 django)
python server.py runserver
可以添加参数,例如 -h 添加主机IP,-p 添加端口,-d 启动调试模式,-r 自动加载 等。
蓝图插件主要实现拆分多个视图函数文件,让同类或同模块分到一个 view 脚本中。例如将不同的模块写成不同的视图函数文件,放在 views 文件夹下。
使用 flask-blueprint 需要先安装插件包
pip install flask-blueprint
使用时在视图函数文件中导入 flask 库的接口 (注意不是 flask-blueprint)
# view.py
from flask import Blueprint
from flask import request
blue = Blueprint('模块蓝图名称', __name__) # 参数2是创建 app 时 Flask 对象的参数名称,即 app 名称。
@blue.route('/index', methods=['GET', 'POST']) # 路由
def index(): # 视图处理函数
pass
蓝图(视图函数)写完之后,需注册到 app 中。
# server.py
# 启动 app 的文件
# 引入视图处理函数的文件
from app.views import view
if __name__ == '__main__':
# 将蓝图对象注册到 flask 服务中
app.register_blueprint(view.blue)
# 启动 app 应用
app.run()
这样就可以将不同模块的视图函数写在不同文件中,然后进行注册就可以了。另外在注册蓝图时,可以添加处理路由的前缀
app.register_blueprint(view.blue, url_prefix='/user')
访问时,则是使用前缀加路由的方式进行访问。
flask-cors 插件主要用来解决跨域访问的问题。CORS的全称是 Cross-Origin Resource Sharing,由w3c组织制定的,现在这个规范,已经被大多数浏览器支持,处理跨域的需求。CORS 在后端设置,所以是一种后端跨域的解决方案。
使用 flask-cors 首先要安装库,然后引入类
pip install flask-cors
from flask_cors import CORS
使用 CORS 时,需要在创建 app 后,app 运行之前使用 CORS 类初始化 app (如果有用蓝图,则需要初始化蓝图)。此方法适用于全局的api接口配置,也就是所有的路由都支持跨域了。
from flask import Flask
from flask_cors import CORS, cross_origin
app = Flask(__name__)
cors = CORS(app)
# 也可以使用 CORS().init_app(app)
from flask import Blueprint
from flask import render_template
from flask_cors import CORS, cross_origin
blue = Blueprint('user',__name__)
cors = CORS(blue)
@blue.route('/')
def fn():
return render_template('html.html')
使用 CORS 类的第一个参数是 app 对象,第二个参数可以是
参数 | 类型 | Head字段 | 说明 |
---|---|---|---|
resources | 字典、迭代器或字符串 | 无 | 全局配置允许跨域的API接口 |
origins | 列表、字符串或正则表达式 | Access-Control-Allow-Origin | 配置允许跨域访问的源,*表示全部允许 |
methods | 列表、字符串 | Access-Control-Allow-Methods | 配置跨域支持的请求方式,如:GET、POST |
expose_headers | 列表、字符串 | Access-Control-Expose-Headers | 自定义请求响应的Head信息 |
allow_headers | 列表、字符串或正则表达式 | Access-Control-Request-Headers | 配置允许跨域的请求头 |
supports_credentials | 布尔值 | Access-Control-Allow-Credentials | 是否允许请求发送cookie,false是不允许 |
max_age | 整数、字符串 | Access-Control-Max-Age | 预检请求的有效时长 |
此种方法适用于配置特定的api接口
from flask import Flask, jsonify
from flask_cors import CORS, cross_origin
app = Flask(__name__)
# 只允许路径为'/login'跨域!
@app.route('/login')
@cross_origin()
def data():
return jsonify({'name':'lxc'})
装饰器的参数有
装饰器参数 | 类型 | Head字段 | 说明 |
---|---|---|---|
origins | 列表、字符串或正则表达式 | Access-Control-Allow-Origin | 配置允许跨域访问的源,*表示全部允许 |
methods | 列表、字符串 | Access-Control-Allow-Methods | 配置跨域支持的请求方式,如:GET、POST |
expose_headers | 列表、字符串 | Access-Control-Expose-Headers | 自定义请求响应的Head信息 |
allow_headers | 列表、字符串或正则表达式 | Access-Control-Request-Headers | 配置允许跨域的请求头 |
supports_credentials | 布尔值 | Access-Control-Allow-Credentials | 是否允许请求发送cookie,false是不允许 |
max_age | 整数、字符串 | Access-Control-Max-Age | 预检请求的有效时长 |
默认情况下,不允许跨站点提交Cookie,如果你希望服务器允许用户跨源发出Cookie或经过身份验证的请求,那只需把supports_credentials 设置为True即可
from flask import Flask, session
from flask_cors import CORS
app = Flask(__name__)
CORS(app, supports_credentials=True)
@app.route("/")
def helloWorld():
return "Hello, %s" % session['username']
@app.after_request
def after(resp):
'''
被after_request钩子函数装饰过的视图函数
,会在请求得到响应后返回给用户前调用,也就是说,这个时候,
请求已经被app.route装饰的函数响应过了,已经形成了response,这个时
候我们可以对response进行一些列操作,我们在这个钩子函数中添加headers,所有的url跨域请求都会允许!!!
'''
resp = make_response(resp)
resp.headers['Access-Control-Allow-Origin'] = '*'
resp.headers['Access-Control-Allow-Methods'] = 'GET,POST'
resp.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type'
return resp
Flask 默认情况下,session 的数据是存在于 cookie 中的,而 cookie 是存在于客户端。所以说 session 是依赖于 cookie 的。客户端修改了 cookie,session 就会失效。所以 flask 自带的 session 是有缺陷的。
通过 flask-session 可以解决此问题,可以将 session 的数据存在数据库或缓存(Redis)中。
pip install flask-session
from flask_session import Session
在 app 配置中加入 session 配置信息,这里使用 settings.py 加载配置
# settings.py
class Dev():
SECRET_KEY = '3JDLLERRMdfsdfp2'
SESSION_TYPE = 'redis'
SESSION_REDIS = Redis(host='127.0.0.1', port=6379,db=1)
# 使用文件存储 session 设置文件目录
# SESSION_FILE_DIR = '/caches/flask_cache'
# 设置缓存文件大小,当超出设置个数,则使用 LRU 算法,删除最近最少使用的缓存文件
# SESSION_FILE_THRESHOLD = 3000
# SESSION_COOKIE_SECURE = True
在应用加载完配置后,创建 session 对象,并加入 app 对象
session = Session()
session.init_app(app)
# 或使用
# Session(app)
初始化完成后,正常使用 session ,数据就会保存到缓存中了。测试访问一下,就可以在 redis 中看到 session 的数据了。
session 会为每个客户端(不同的浏览器、ajax、类似postman的测试工具)都创建一个独立的 session,但是无法判断 session 是属于具体哪个客户端的。Flask 在 HTTP 协议中已考虑此问题,解决方案是给 cookie 设置一个 session_id。因为 cookie 是和客户端关联的,所以不同的客户端有不同的 session_id,服务端以此 session_id 来确定当前请求是属于哪个会话。
尽管 session 存储到服务端,但是获取 session 数据所需要的 session_id 还是依赖于 cookie 。当禁用了 cookie 后,就无法正常获取 session 了。所以 flask-session 不能替代 cookie。
SQLAlchemy 是 python 中一个比较强大的 ORM 框架,其功能全面,使用简单。flask-sqlalchemy 是为 flask 应用添加 sqlalchemy 支持的扩展插件。部分可以参考 SQLAlchemy 的文档
SQLAlchemy 官方站(英文)
支持的数据库及连接方式
先安装 flask-sqlalchemy,注意 flask-sqlalchemy 需要依赖 mysqlclient 或 pymysql 来操作 mysql 数据库。
pip install flask-sqlalchemy
from flask-sqlalchemy import SQLAlchemy
然后可以对 flask-sqlalchemy 进行配置,可以在配置文件(settings.py)中进行配置
配置项 | 说明 | 示例 | 备注 |
---|---|---|---|
SQLALCHEMY_DATABASE_URI | 数据库连接地址 | app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqldb://user:[email protected]:3306/dbname?charset=utf8' |
如果数据库使用pymysql,则协议名需要将 mysql+mysqldb 改为 mysql+pymysql |
SQLALCHEMY_BINDS | 访问多个数据库时,用于设置数据库的连接地址 | ||
SQLALCHEMY_ECHO | 是否显示底层执行的SQL语句 | app.config['SQLALCHEMY_ECHO'] = True |
|
SQLALCHEMY_RECORD_QUERIES | 是否记录执行的查询语句,用于慢查询分析,调试模式下自动启动 | ||
SQLALCHEMY_TRACK_MODIFICATIONS | 是否追踪数据库变化(触发钩子函数),会消耗额外内存 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False |
|
SQLALCHEMY_ENGINE_OPTIONS | 设置针对 sqlalchemy 本体的配置项 | ||
SQLALCHEMY_COMMIT_ON_TEARDOWN | 发生异常或事务结束时是否自动提交 | app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True |
在完成配置后,app 运行前可以创建并初始化 sqlalchemy 对象。初始化的方法有两种
# 使用对象关联app
db = SQLAlchemy()
db.init_app(app)
# 使用 app 创建对象
db = SQLAlchemy(app)
推荐使用第一种,然后在模型包中创建 SQLAlchemy 对象,在应用中导入该对象并关联至 app
在项目目录中创建一个 models 目录,然后所有的模型文件可以放在此目录下。推荐将此目录创建成包,方便导入和使用。
# models/__init__.py
from from flask-sqlalchemy import SQLAlchemy
# 创建 SQLAlchemy 对象
db = SQLAlchemy()
# app/__init__.py
from flask import Flask
from models import db
import settings
# 创建 app 对象
app = Flask(__name__)
# 加载设置
app.config.from_object(settings.Dev)
# 初始化 SQLAlchemy 对象
db.init_app(app)
根据需要操作的数据库表创建相应的模型类,之后对模型类的操作就是对数据库表的操作。默认情况下,类名即数据库的表名,类属性名即为字段名。
# models/__init__.py
from from flask-sqlalchemy import SQLAlchemy
# 创建 SQLAlchemy 对象
db = SQLAlchemy()
# 创建模型类,继承自 db.Model
# 默认类名称即表名,但是也可以使用 __tablename__ = '表名称' 来定义表名
class User(db.Model):
# 声明属性,即数据库中表的字段
# 默认属性名即字段名,若不同可以将字段名作为参数传入 uid = db.Column('id')
id = db.Column(db.Integer, primary_key=True, autoincrement=True) # 整型、主键、自增长
number = db.Column(db.String(18), unique=True) # 唯一
name = db.Column(db.String(20), nullable=False) # 不能为空
phone = db.Column(db.String(11))
password = db.Column(db.String(100), nullable=False)
def __str__(self): # 自定义交互模式,即 print() 函数的打印内容
return '(%s, %s, %s, %s, %s)' % (self.id, self.number, self.name, self.phone, self.password)
常用的字段类型
类型名 | python接收类型 | mysql生成类型 | 说明 |
---|---|---|---|
Integer | int | int | 整型 |
Float | float | float | 浮点型 |
Boolean | bool | tinyint | 整形,1字节 |
Text | str | text | 文本,最大64KB |
LongText | str | longtext | 文本,最大4GB |
String | str | varchar | 变长字符串,需传参限定长度 |
Date | datetime.date | date | 日期 |
DateTime | datetime.datetime | datetime | 日期和时间 |
Time | datetime.time | time | 时间 |
ForeignKey | 外键约束,需传参指定主键 |
常用的字段参数选项
选项名 | 说明 |
---|---|
primary_key | 主键,自增 |
unique | 唯一约束 |
nullabel | 非空约束,默认为True |
default | 默认值 |
index | 创建索引 |
autoincrement | 自增 |
如果模型中需要使用不属于数据库的字段数据,则可以添加成员函数。需注意的是,此成员不和数据库关联,所以无法进行修改、删除等操作。
@property
def birthday(self):
return self.number[6:14]
在视图函数内部可以引入并使用模型,来进行数据库的增删改查操作。
如果无法正确导包,可以使用 sys.path.extend()
方法,将包路径放入一个列表,然后传入。使用模型时,如果提示错误(no application found),可以加入 with app.app_context().push():
。
# 导入模型
from models import User
# 1.创建模型对象,此对象就是1条记录。
# 在参数中可以添加数据
user = User(number='410300200001010101', name='张三', password='1234567890')
# 也可以使用设置对象属性的方式设置数据
# user.number = '410300200001010101'
# user.name = '张三'
# import hashlib
# user.password = hashlib.md5('1234567890'.encode('utf-8)).hexdigest()
# 2.将模型对象添加到会话中
db.session.add(user)
# 也可以添加多个对象,即多条记录
# db.session.add_all([user1, user2, user3])
# 3.提交会话(会提交事务)
# sqlalchemy 会自动创建隐式事务
# 事务失败会回滚
db.session.commit()
需注意的是,这里使用的会话不是状态保存机制中的 session,而是 sqlalchemy 的会话。它被设计为数据操作的执行者,从 SQL 角度则可以理解为是一个加强版的数据库事务。
使用 db.session.query()
方法,返回重新组织的临时表(视图)。可以将需要返回的表或字段作为参数传入,过滤和筛选则使用链式调用相应的方法。
# 所有数据只范围两个字段信息
db.session.query(User.name, User.number).all()
db.session.query(User)
是对单表的查询,可以简单使用表模型对象进行查询。返回的数据是符合查询条件的整条数据。
# 导入模型
from models import User
# 查询所有数据
User.query.all() # 返回列表,元素为模型对象
# 返回查询第一条数据
User.query.first() # 返回模型对象或None
# 按照主键查询,值为4的记录
User.query.get(4) # 返回模型对象或None
# 使用过滤器,查询字段,目标为 id=4 的数据
User.query.filter_by(id=4).all() # 返回 BaseQuery 对象,可以续接其他过滤器/执行器,如 all/count/first 等
# 复杂过滤器,参数为比较运算/函数引用等
User.query.filter(User.id == 4).first() # 返回 BaseQuery 对象
# 复杂查询的一些方法
User.query.filter(User.name.endswith('g')).all() # 以 g 结尾
User.query.filter(User.name.startswith('w')).all() # 以 w 开始
User.query.filter(User.name.contains('n')).all() # 包含 n
User.query.filter(User.name.like('w%n%g')).all() # 模糊查询,使用通配符
# 复杂查询,逻辑且
User.query.filter(User.name.startswith('li'), User.phone.endswith('04')).all()
from sqlalchemy import and_ # 也可以使用数据库对象 db.and_
User.query.filter(and_(User.name.startswith('li'), User.phone.endswith('04'))).all()
# 复杂查询,逻辑或
from sqlalchemy import or_ # 也可以使用数据库对象 db.or_
User.query.filter(or_(User.name.startswith('zh'), User.number.endswith('2212'))).all()
# 复杂查询,逻辑非
User.query.filter(User.name != 'wang').all()
from sqlalchemy import not_ # 也可以使用数据库对象 db.not_
User.query.filter(not_(User.name == 'wang')).all()
# 复杂查询,条件在集合内
User.query.filter(User.id.in_([1, 3, 6, 9])).all()
# 使用字符串表达式附带参数
User.query.filter(User.name != :w).params(w='wang').all()
# 排序,默认升序,可以使用 desc() 方法指定降序
User.query.order_by(User.name, User.number.desc()).all() # name 升序,number 降序
# 分页,原始方式
User.query.order_by(User.id).offset(3).limit(4).all() # 按 id 排序,跳过前3条数据,从第4条开始返回4条数据
# 分页,简单方式,每页3条,查询第2页数据 paginate(页码, 每页大小)
pn = User.query.paginate(2, 3)
pn.pages # 总页数
pn.page # 当前页码
pn.items # 当前页的数据
pn.total # 总条数
模型查询也可以只返回某些字段
# 导入模型
from models import User
from sqlalchemy.orm import load_only
User.query.options(load_only(User.name, User.number)).all()
SQLAlchemy 还支持使用原生 sql 语句进行查询
# 原生 sql 语句查询,每行记录为对象的 corsor 属性
rst = db.session.execute('select * from user')
for row in rst.cursor:
print(row)
聚合查询可以使用 session.query()
方法,参数可以不传入表或字段,而是聚合查询函数。也可以使用模型对象加聚合方法来查询。
# 统计 Photo 表中的记录数量,返回元组
db.session.query(db.func.count(Photo.id)).all() # count 的参数必须传入一个字段,将按照这个字段进行统计
Photo.query.count()
# 参数传入表对象,即对此表进行聚合查询
# label() 可以重命名查询字段(别名)
db.session.query(User, db.func.count('*').label('cnt')).all() # 返回 User 表数据,并添加字段 cnt,值为数据数量
# 统计数量并进行分组(对分组统计数量)
db.session.query(User.name,db.func.count(User.id).label('cnt')).group_by(User.name).order_by(db.Column('cnt').desc()).all()
# 统计和
db.session.query(User,db.func.sum(Card.money).label('cnt')).all
常见的聚合方法有
func 也可以直接导入使用 from sqlalchemy import func
可以使用 group_by()
方法按照某一字段进行分组,也可以在分组后使用 having()
方法设置分组条件。
使用 exists()
或 any()
方法可以返回查询数据是否存在
from models import User, Photo
db.session.query(User).filter(db.exists().where(Photo.user_id == User.id)).all()
db.session.query(User).filter(User.photos.any()).all()
可以联合多表进行查询
from models import User, Photo
db.session.query(User.name, Photo,name).filter(User.id==Photo.user_id).all()
# 内连接
db.session.query(Photo.name, User.name).join(User).all()
# 外连接
db.session.query(User.name).outerjoin(Photo).all()
修改数据需要先拿到数据,即先查询
# 导入模型
from models import User
# 获取要修改的数据(对象)
user = User.query.get(4)
# 修改数据
user.name = '王五'
user.phone = '13939939999'
# 将模型对象添加到会话中,提交事务时默认会检测数据是否变化,所以一般可以省略
db.session.add(user) # 或使用 db.session.is_modified(user) 来判断对象是否修改了属性,需要更新
# 提交事务
db.session.commit()
此种方法将查询和更新分离,效率低一些,且如果并发进行可能会出现更新丢失问题。也可以基于过滤条件同时更新,这样效率更高,且具有原子性。
# 导入模型
from models import User
User.query.filter(User.id == 4).update({'name' : '赵六'})
db.session.commit()
同修改数据,也分为先查询再删除和同时删除两种方式
# 导入模型
from models import User
# 获取要修改的数据(对象)
user = User.query.get(4)
# 添加删除事务
db.session.delete(user)
# 提交事务
db.session.commit()
# 导入模型
from models import User
User.query.filter(User.id == 4).delete()
db.session.commit()
session 被设计为数据执行的操作者,会将操作产生的数据保存到内存中,然后等刷新操作时再同步到数据库。但是有两种情况下会隐式执行刷新操作:提交会话和执行查询。除此之外则需要手动刷新事务: db.session.flush()
# 判断当前数据库会话是否可用
db.session.is_active
# 回滚事务
db.session.rollback()
# 提交事务
db.session.commit()
# 刷新数据
db.session.refresh(obj)
一对一关系和一对多关系只需要设置一个外键字段就可以关联了,这里主要研究一对多关系。
MYSQL的一对多关系需要使用第三张关系表,指明关联的外键。在 flask-SQLAlchemy 中,就是创建一个关系表(非模型类)来实现
# 创建用户和角色的关系表
user_role = db.Table('user_role', # 表名称
Column('user_id', Integer, ForeignKey('user.id', name='user_role_fk')), # 关联到user表的外键
Column('role_id', Integer, ForeignKey('role.id', name='user_role_pk'))) # 关联到role表的外键
然后在一个多端表中,指明使用关系即可
# user 表的数据模型
class User(Base):
auth_key = Column(String(100), nullable=False)
nick_name = Column(String(20))
# 多对多关系,指定 secondary 设置关联表Table
roles = db.relationship(Role, secondary=user_role) # 和 Role 建立关系,关系使用关系表 user_role
当定义了 app 后,可以在视图函数中输出日志。例如在控制台输出信息
# 使用 flask 中的日志记录器
app.logger.info('这里输入日志信息,字符串格式')
或使用 logging 产生日志,保存到文件或发送到日志服务器
import logging
from logging import StreamHandler, FileHandler
from logging.handlers import HTTPHandler, TimedRotatingFileHandler
from logging import Formatter
# 设置日志记录器,名称为 edu_api
logger = logging.getLogger('edu_api')
def config_log():
# 设置日志记录器格式,asctime(时间),name(日志记录器的名称),levelname(日志等级),message(日志信息)
fmt = Formatter(fmt='%(asctime)s %(name)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
# 日志处理器
iohandler = StreamHandler()
iohandler.setLevel(logging.DEBUG)
iohandler.setFormatter(fmt)
# 文件处理器
file_handler = FileHandler('edu.log')
file_handler.setLevel(logging.WARN)
file_handler.setFormatter(fmt)
# http处理器
http_handler = HTTPHandler(host='localhost:5000', url='/log', method='POST')
http_handler.setLevel(logging.ERROR)
http_handler.setFormatter(fmt) # 上传的数据不需要 fmt 格式,所以可以省略
logger.setLevel(logging.DEBUG)
logger.addHandler(iohandler)
logger.addHandler(file_handler)
logger.addHandler(http_handler)
# 执行创建日志的函数
config_log()
# 输出一些日志信息
logger.info('输出日志信息')
logger.warning('输出提示信息')
logger.error('输出错误信息')
logger.critical('输出严重信息')
# 删除日志处理器
# logger.removeHandler(file_handler)
在创建了 app 并加载好设置后,app 运行前,可以设置日志记录器
from flask import Flask
import settings
from flask.logging import default
# 创建此app对象
app = Flask(__name__)
# 加载配置
app.config.from_object(settings.Dev)
# 设置日志记录器
app.logger.setLevel(logging.DEBUG)
app.logger.addHandler(http_handler)
flask-bootstrap 是集成了 bootstrap 的插件,方便的地方是内置了一些基模板,
pip install flask-bootstrap
flask-bootstrap英文文档
使用时继承基模板 base.html 就可以了。
flask-cache 可以进行缓存优化加载,减少数据库的 IO 操作
flask-cache 英文文档
支持 RESTful 的插件