初识Flask
Flask的介绍
由于我只是刚在学习Flask,这个文章只是作为的学习笔记之用。
官方中文文档:https://dormousehole.readthedocs.io/en/latest/quickstart.html
Flask的安装
pip install flask
导入Flask
from flask import Flask
创建Flask项目
如果使用pychram专业版可以直接创建Flask项目,创建完成会自动创建几个文件:
- templates --用于存放模板的文件夹
- static --用于存放静态文件的文件夹
- app.py --一个最小的Falsk应用
不用pychram专业版也能创建Flask,手动创建以上文件即可
第一个Flask应用
使用Pychram创建Flask项目,app.py就有几行代码了,运行它就可以得到最初的一个Flask应用
"""代码如下"""
from flask import Flask # 首先我们导入了Flask 类。该类的实例将会成为我们的WSGI 应用。
"""
接着我们创建一个该类的实例。第一个参数是应用模块或者包的名称。
如果你使用 一个单一模块(就像本例),那么应当使用 __name__ ,
因为名称会根据这个模块是按应用方式使用还是作为一个模块导入而发生变化
(可能是 ‘__main__’ , 也可能是实际导入的名称)。
这个参数是必需的,这样Flask才能知道在哪里可以找到模板和静态文件等东西。
"""
app = Flask(__name__)
@app.route('/') # 然后我们使用route()装饰器来告诉Flask触发函数的URL 。
def hello_world():
return 'Hello World!' # 函数最后返回需要在用户浏览器中显示的信息。
if __name__ == '__main__':
app.run() # 最后是启动服务
#注释抄录自Flask中文文档
运行app.py文件,在浏览器输入http://127.0.0.1:5000/ ,就可以看到Hello, World!了
Flask的debug模式
这里做一个对比,如果不开启debug模式,当执行到错误代码时,页面会怎么显示:
"""错误的代码"""
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
a = "ABC" # a是字符串
b = int(a) # b要接收的是一个整数的a
return 'Hello World!'
if __name__ == '__main__':
app.run()
结果:
页面中提示的是server Error
当如果开启debug模式后,运行同样的代码:
进入的是一个错误提示页面
这就是debug模式,Flask默认情况下是关闭debug模式的,但是在开发环境中可以开启debug模式,方便调试。另外开启debug模式后,每次保存代码时,Flask服务会自动重启。正式环境不可以开启debug模式!
开启debug模式的三种方式
1.run()增加debug=True参数
参看run()方法的源码可以发现,方法内置了一个debug参数,只要将debug=True传入,即可开启debug模式
"""代码如下"""
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
debug模式开启之后这里会显示Debug mode: on
2. app.debug = True
"""代码如下"""
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
a = "abc"
b = int(a)
return 'Hello World!'
if __name__ == '__main__':
# app.run(debug=True)
app.debug = False # 使用app.debug = True 也可以开启debug模式
app.run()
3. 使用配置文件
"""代码如下"""
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
a = "abc"
b = int(a)
return 'Hello World!'
if __name__ == '__main__':
# app.run(debug=True)
# app.debug = False
app.config.update(DEBUG=True) # 使用配置文件开启debug模式
app.run()
需要注意的是,在执行这段代码时,其实不需要特意去建一个config文件,因为Flask本身就内置了一个Config类并且是继承字典(dict)的一个子类,在使用app.config就是实例化flask.config.Config.。因此当调用config方法时完全可以像字典一样操作。
在页面中调试
开启debug后,报错信息的页面右边会有一个小黑框,点这个可以进入调试模式
点击后需要输入一个PIN
PIN的来源如下:
然后输入PIN之后,回到网页,在下方就可以看到一个console ready,这里可以做些代码的调试。
注意:输入PIN后,Werkzeug会将PIN作为cookie的一部分保存起来,有效期为8个小时,有效期内无需在输入,过期失效。这样可以防止有人利用调试模式对网站进行攻击。
配置管理
内置配置变量
上面的代码运行的时候都是直接执行app.py文件,但是官方文档里面提到可以使用python -m flask run 来运行Flask应用。
但是在使用此命令运行前,需要先配置环境变量:
Linux环境:
export FLASK_APP=app.py # 设置运行的py文件
python -m flask run # 运行Flask应用
Windows环境:
set FLASK_APP=app.py # 设置运行的py文件
python -m flask run # 运行Flask应用
另外如果使用python -m flask run运行Flask应用并且想打开debug模式,还得做如下设置:
Linux环境:
export FLASK_ENV=development # 打开所有开发模式(包括debug)
#或者只是单据打开debug模式
export FLASK_DEBUG=1 # 单独打开debug模式
Windows环境:
set FLASK_ENV=development # 打开所有开发模式(包括debug)
#或者只是单据打开debug模式
set FLASK_DEBUG=1 # 单独打开debug模式
其余的变量可以参考官方文档
配置文件
由于配置项确实有点多,为了方便使用可以通过将配置项放在同一个文件下,然后在代码中进行加载,但要注意必须要在运行应用前加载完配置变量。
创建一个settings.py文件,然后在里面写入
DEBUG=True
然后在代码中使用config.from_object()载入配置文件:
import settings.py # 需要将配置文件导入
...
...
if __name__ == '__main__':
app.config.from_object('settings') # 载入配置文件
app.run
除了使用config.from_object()方法之外,还可以使用config.from_pyfile()方法:
if __name__ == '__main__':
app.config.from_pyfile('settings.py, silent=True')
- 使用config.from_pyfile()方法不需要事先导入settings文件,但是传参时需要将文件名的后缀加上;
- config.from_pyfile()除了加载.py之外,还可以加载ini等其他后缀格式的文件;
- silent参数的意思是静默模式,等于True时,如果settings文件不存在,不会抛出异常。
另外某些原因不能在代码中就指定settings文件的路径,可以使用confing.envvar()方法,使用此方法可以在运行Flask应用前,在命令行先指定配置文件路径,再运行Flask应用。
首先app.py的代码如下:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
# a = "abc"
# b = int(a)
return 'Hello World!'
if __name__ == '__main__':
app.config.from_object('settings') # 配置文件的名称(不需要后缀名)作为参数传入
app.config.from_envvar('SETTINGS') # 将配置文件的名称的大写作为参数传入,这个参数会作为配置变量使用
app.run()
Windows:
set SEETINGS="E:\Flask项目学习\settings.py"
python app.py
Linux:
export SEETINGS=/Flask项目学习/settings.py
python run-app.py
- 使用这个方法也能导入其他后缀名的文件(ini、cfg等)
路由配置
使用方法
Flask的路由使用方法非常简单,只需要在函数前绑定route()装饰器即可
from flask import Flask
app = Flask(__name__)
@app.route('/') # url:http://127.0.0.1:5000/
def index():
return 'Index'
@app.route('/hello/') # url:http://127.0.0.1:5000/hello/
def hello_world():
return 'hello world'
if __name__ == '__main__':
app.run(debug=True)
变量规则
route()里面的值并非固定死的,通过URL的一部分标记为<variable_name> 就可以在URL上添加变量,下面的代码中<name>作为标记部分会传入到函数中,因此函数需要增加一个name的形参来接收它。
from flask import Flask
app = Flask(__name__)
@app.route('/hello/<name>/')
def hello_world(name):
return name
if __name__ == '__main__':
app.run(debug=True)
默认情况<variable_name> 是string类型的,但可以通过使用 <converter:variable_name>加上一个转换器,为变量增加规则。
convert的类型有以下几种:
- string: 默认的数据类型,接受没有任何斜杠/的字符串。
- int: 整形
- float: 浮点型。
- path: 和string类似,但是可以传递斜杠/。
- uuid: uuid类型的字符串。
- any:可以指定多种路径
举例:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Index'
@app.route('/hello/<string:name>/') # 默认的就是这种格式,和<name>相等
def hello_string(name):
return "这是string:"+name
@app.route('/hello/<int:name>/')
def hello_int(name):
return "这是int:"+name
@app.route('/hello/<path:name>/')
def hello_path(name):
return "这是path:"+name
@app.route('/any/<any(dog, cat):url>/')
def hello_any(url):
return "这是any:"+url
if __name__ == '__main__':
app.run(debug=True)
除了上面的方式,平常网页里看到最多的是用?后面跟着参数,例如在百度搜索python,然后再看看URL:
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=python
?后面跟着的就是参数,每个参数用&隔开。
接下来模仿一下这种传参方式:
from flask import Flask
from flask import request # 导入request模块
app = Flask(__name__)
@app.route('/')
def index():
return 'Index'
@app.route('/wd')
def baidu():
return request.args.get('name') # 将name的值返回到网页中显示
if __name__ == '__main__':
app.run(debug=True)
结果:
反转函数(url_for)
一般情况下,通过URL就可以找到某个对应的函数,然后执行该函数。但是如果反过来,通过函数名找对应的url,这里就可以用到url_for()。
url_for()用于构建指定函数的URL,函数名作为第一个参数,从第二个参数开始为关键字参数,可以传入多个,每个关键字参数对应URL的变量。未知(未定义)的参数将添加到URL作为查询参数。
在重定向和跳转页面时,就会经常用到反转函数。
这里做个简单的需求,有两个url,当我输入index后,在控制台输出page2的url
from flask import Flask
from flask import url_for # 需要先导入url_for
app = Flask(__name__)
@app.route('/index/') # index的url: /index/
def index():
print(url_for("page2", aid=2)) # 用url_for获取page2的url并打印在控制台,第一个参数是函数的名字
return "首页"
@app.route('/page2/<aid>') # page2的url: /page2/<aid>
def page2(aid):
return "aid=: %d" % aid
if __name__ == "__main__":
app.run()
运行结果:成功打印了page2的路由
- 从上面的例子可以看出,url_for()方法其实就是在通过page2这个函数名,然后再加上aid反向得出了/page2/2这个url地址。
那为什么要用反转函数?
其实通过上面的例子可以看出,用反转函数url_for()来动态构建url的话,可以减少代码修改的量,特别是在做重定向或者跳转的时候,不管你的url地址怎么改变,只要函数名不变,就可以通过url_for来找到URL。下面是其特性的描述,来源于官方文档:
- 反转通常比硬编码 URL 的描述性更好。
- 你可以只在一个地方改变 URL ,而不用到处乱找。
- URL 创建会为你处理特殊字符的转义和 Unicode 数据,比较直观。
- 生产的路径总是绝对路径,可以避免相对路径产生副作用。
- 如果你的应用是放在 URL 根路径之外的地方(如在 /myapplication 中,不在 / 中), url_for() 会为你妥善处理。
指定HTTP方法
Flask默认情况下,所有的url请求都是GET请求,如果要使用其他请求可以再route()里加上methods=[‘GET’,‘POST’…]
@app.route("/", methods=['GET', 'POST'])
实验一下接收GET和POST请求
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route("/login/", methods=['GET', 'POST'])
def login():
print(request.args.get('username')) # 接收GET请求
print(request.form.post('name')) # 接收GET请求
return "login"
if __name__ == '__main__':
app.run()
重定向及页面跳转
重定向分为永久性重定向和暂时性重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面。比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应该给他重定向到登录页面。
- 永久性重定向:http的状态码是301,多用于旧网址被废弃了要转到一个新的网址确保用户的访问,最经典的就是京东网站,你输入www.jingdong.com的时候,会被重定向到www.jd.com,因为jingdong.com这个网址已经被废弃了,被改成jd.com,所以这种情况下应该用永久重定向。
- 暂时性重定向:http的状态码是302,表示页面的暂时性跳转。比如访问一个需要权限的网址,如果当前用户没有登录,应该重定向到登录页面,这种情况下,应该用暂时性重定向。
在flask中,重定向是通过flask.redirect(location,code=302)这个函数来实现的,location表示需要重定向到的URL,应该配合之前讲的url_for()函数来使用,code表示采用哪个重定向,默认是302也即暂时性重定向,可以修改成301来实现永久性重定向。
实现一个需求:
登录个人中心,如果个人中心没有携带用户名,那就重定向到登录页面
from flask import Flask
from flask import request
from flask import redirect
app = Flask(__name__)
# 登录页面
@app.route('/login/')
def login():
return "login"
@app.route("/profile/")
def profile():
name = request.args.get("name")
if name:
return name
else:
return redirect("/login/") # 重定向到登录页面
if __name__ == '__main__':
app.run()
在浏览器输入http://127.0.0.1:5000/profile/,发现通过以上的代码就简单的实现了页面重定向。但是这种方法属于硬编码方式,当我把@app.route(’/login/’)改为@app.route(’/sigin/’),这时还得把重定向登录页面的代码改为return redirect("/sigin/"),这样代码改动的地方就会增多,因此可以结合反转函数url_for()使用.
代码修改如下:
from flask import Flask
from flask import request
from flask import redirect
from flask import url_for
app = Flask(__name__)
# 登录页面
@app.route('/login/')
def login():
return "login"
@app.route("/profile/")
def profile():
name = request.args.get("name")
if name:
return name
else:
return redirect(url_for('login')) # 重定向到登录页面
if __name__ == '__main__':
app.run()
改完之后,不管登录页面的url做如何的改变,都只需要改动route()即可,其他地方就不需要再改动了。毕竟代码改动的越少,出现BUG的几率越低。
- redirect可以增加参数code=301,设置为永久重定向,默认是302。
模板
模板是一个web开发必备的模块。因为我们在渲染一个网页的时候,并不是只渲染一个纯文本字符串,而是需要渲染一个有富文本标签的页面。这时候我们就需要使用模板了。在Flask中,配套的模板是Jinja2,Jinja2的作者也是Flask的作者。这个模板非常的强大,并且执行效率高。
模板导入
首先在使用模板前需要导入模板,但是模板通常是一个html文件,并非py文件,因此不能使用平常的import导入,Flask提供了导入模块render_template模块。
另外,在使用前需要先在项目目录下创建templates文件夹(用pychram专业版创建Flask项目会自动创建这个文件夹),然后将模板文件都放到该文件夹内,然后就可以使用render_template模块,该模块默认会去templates文件夹下寻找指定的模板,因此不推荐将模板存放到其他文件夹内。
示例:
将index.html文件放到templates文件夹内
然后在代码里导入模板
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html") #导入模板
if __name__ == '__main__':
app.run()
另外,一个正常的网页通常会有很多个模板,但是这些模板如果都templates文件夹内就会显得很乱,因此通常会对templates下再创建一个多文件夹,分类每个模板,如下:
然后回到代码里,render_template()默认是在templates文件夹内找模板,但只会找第一层目录的,像这样底下还有多层文件夹的,它并不会再往下去找,不过要解决也很简单,需要把路径也上就行。
@app.route("/")
def index():
return render_template("index/index.html")
像这样,它就会去index文件下内找到index.html模板
修改模板默认目录
上面说的render_template会默认到templates文件夹下找模板,但是这个默认值时可以修改的。不过非常不推荐修改,因为大家的共识就是templates存放模板的,模板都是存放到templates里面,如果改成其他文件夹了,那与你一起开发的同伴可能就找不到了。
修改方法:
from flask import Flask
app = Flask(__name__, template_folder="路径")
后台传参到前端
一般情况下,python做的是后台的工作,模板是前台展示的效果,那么后台要值给前端就需要用到jinjia2。
jinjia2的传参方式与VUE、Django的传参方式类似,在html里也是用接收“{{}}”参数。
测试传参:
py代码如下:
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html", username=“测试传参”, home=“中国”) # 传入参数,这里可以写多个参数
if __name__ == '__main__':
app.run()
html代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<h1>{{ username }}</h1>
<h2>{{ home }}</h2>
</body>
</html>
以上的代码就实现了传参,但是如果传的参数有很多的时候,还可以使用字典的格式传参
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/")
def index():
# 创建字典
context = {
'username': "多个参数传参",
'home': "中国",
'age': 18,
}
return render_template("index/index.html", context=context) # 传入字典
if __name__ == '__main__':
app.run()
通过这种方式传参,还需要修改下HTML的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<h1>{{ context.username }}</h1>
<h1>{{ context.age }}</h1>
<h1>{{ context.home }}</h1>
</body>
</html>
由于我们传入的是一个字典,因此也导致了我们需要修改HTML的代码,但是如果想更加便捷,html代码不改,还是用{{ username }}来接收参数,可以使用到**context
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/")
def index():
context = {
'username': "多个参数传参",
'home': "中国",
'age': 18,
}
return render_template("index/index.html", **context) # 这里改为**context
if __name__ == '__main__':
app.run()
html改回原来的:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<h1>{{ username }}</h1>
<h1>{{ age }}</h1>
<h1>{{ home }}</h1>
</body>
</html>
这样做也可以实现同样的效果
- **context这里的**是解包的意思,他会将字典的键值对转化为 username=“多个参数传参”,因此在html上就可以直接通过{{ username }}接收值了
- 在jinjia2的语法中,如果要在html上注释,可以使用这个{* 代码 *}
宏和import
宏
模板中的宏和函数类似,可以传递参数但不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把不固定的值抽出来变成一个变量。
宏的定义
{% macro 宏名称(参数) %}
内容
{% endmacro %}
宏的用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% macro input(name, value='', type='text') %}
<input name="{{ name }}" value="{{ value }}" type="{{ type }}">
{% endmacro %}
<table>
<tr>
<td>用户名:</td>
<td>{{ input('username') }}</td>
</tr>
<tr>
<td>密码:</td>
<td>{{ input('password', type='password') }}</td>
</tr>
</table>
</body>
</html>
- 先定义了一个宏,宏的名字是input,它有3个参数
- 宏的内容就是一个input标签,input的3个属性对应宏的3个参数
- 然后就可以调用宏了,只需要给宏传入参数就可以使用了
- 通过这种方式,只需要写一次input标签,然后通过传参的方式就实现了多个input标签
import
由于一般情况下宏会有很多个,所以通常会将宏都放入一个文件里,然后通过import导入的方式导入宏。
import的用法
用上面写的宏代码为例,演示两种导入方式
1. import … as … 形式导入宏
{% import 'index/macro.html' as macro %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<tr>
<td>用户名:</td>
<td>{{ macro.input('username') }}</td>
</tr>
</body>
</html>
- 直接用import导入macro.html文件需要用as取个名字才能使用
2. from … import … as … / from … import …
{% from 'index/macro.html' import input %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<tr>
<td>用户名:</td>
<td>{{ input('username') }}</td>
</tr>
</body>
</html>
- 用from…import…的方式导入,可以直接使用
include和set语句
include
include可以把模板的内容引入到另一个模板里,类似于将一个模板的代码复制到另一个模板的指定位置
创建两个html文件,分别名称header,footer
header.html写入如下代码
<h1>表头</h1>
<ul>
<li>首页</li>
<li>新闻</li>
<li>图片</li>
</ul>
footer.html写入如下代码
<h1>页脚</h1>
然后在import.html文件中插入include语句
{% from 'index/macro.html' import input %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% include "index/header.html" %}
<tr>
<td>用户名:</td>
<td>{{ input('username') }}</td>
</tr>
{% include "index/footer.html" %}
</body>
</html>
打开import页面后,就会发现他将header和footer这两个文件的内容都复制到页面上来了
set语句
用于在模板中添加变量,平常要给模板添加变量需要在视图里渲染过去,当如果用set可以直接在模板中添加一个变量。
set添加的变量可以为字符串,也可以为字典和列表。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% set name="test" %}
<p>{{ name }}</p>
</body>
</html>
- 这样添加set变量是全局的
- 如果视图也传了同样一个变量,最终显示的是模板set的变量值
局部的set变量可以这样写
{% with %}
{% set name="这是局部的" %}
<p>{{ name }}</p>
{% endwith %}
也可以简写为
{% with name="局部的.简写" %}
<p>{{ name }}</p>
{% endwith %}
- 变量名相同时,全局的不会影响局部
继承(extend)
模板继承是项目中最常用的方式,他就想python的类一样,子模板可以继承父模板所有的东西,并且子模板也能重写父模板的内容。通过这种继承的方式,可以简化代码,增加代码复用性。
- 在父模板中定义一个block,子模板就可以重写这个block
继承的使用方法
先定义一个父模板base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% block header %}
<ul>
<li>网站头部</li>
<li>首页</li>
<li>新闻</li>
<li>图片</li>
</ul>
{% endblock %}
{% block content %}
<p>网站内容</p>
{% endblock %}
{% block footer %}
<div class="footer">
<h1>网站底部</h1>
</div>
{% endblock %}
</body>
</html>
然后创建一个子模板index.html,由于继承相当于将父模板的所有东西会复制到子模板里,所以子模板的html标签可以省略不写。
{% extends "inherit/base.html" %}
上面仅仅一句话就可以将父模板都继承过来。
但是要注意,此时在子模板自己添加一些HTML标签也不会生效。
只能通过调用父模板的block,才可以对页面做修改
{% extends "inherit/base.html" %}
{% block header %}
<p>这是index页面</p>
{% endblock %}
{% block content %}
<p>index页面主体内容</p>
{% endblock %}
{% block footer %}
<p>index页面尾部</p>
{% endblock %}
- 当某块内容在每个页面显示都不一样的情况下,最好在父模板中在这块定位为block,哪怕block为空也可以,让子模板去重写就行了。
- 子模板不能继承多个父模板
- block可以嵌套使用,但不能重名
模板中的super()
模板中的super()方法和python的类的super()方法是同样的意思,在继续父类后,重写父类的方法但也要调用父类的方法,这时用的就是super()。
在模板中的使用方法也很简单,只需要加上super()就可以了。
{% block header %}
{{ super() }}
<p>这是index页面</p>
{% endblock %}
静态文件的配置
通常网站中的css、js、图片等文件都会存放在静态资源目录static。
在模板中是使用url_for方法加载静态文件。
加载CSS:
<link rel="stylesheet" href="{{ url_for('static', filename="css/index.css") }}">
加载图片:
<img src="{{ url_for('static', filename='img/v6.0课程大纲.png') }}" alt="">