Flask基础

## 简介 ### 项目目录 ![image-20210207111647955](http://qiniu.rainna.xyz/image-20210207111647955.png)
1
2
3
static 静态文件存放路径
template 模板文件存放路径
app.py 可改名,编写网页逻辑
### HelloWorld
1
2
3
4
5
6
7
8
9
from flask import Flask
app = Flask(__name__) # 创建一个网页app

@app.route('/') # 网页路由
def hello_world(): # 响应函数
return 'Hello World!'

if __name__ == '__main__':
app.run() # 运行
## 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask

# 配置文件路径
app = Flask("myApp", static_folder="static", template_folder="templates")


@app.route('/config')
def hello_world():
return 'Hello World!'


if __name__ == '__main__':
# 运行配置,启动debug模式
app.run(debug=True, port=8080, host="0.0.0.0")
- 或在终端运行时配置
1
2
> set FLASK_ENV=development
> flask run
- pycharm的flask模板运行flask项目会有很多问题,用纯python模板!! ### Flask类
1
2
3
4
root_path		# 主模块所在的目录的绝对路径
static_folder # 静态资源路径,默认是当前app下的root_path/static
static_url_path # 访问静态资源的路径,默认为static_folder的属性值
template_folder # 模板文件的文件名
- `import_name`: 应用程序的另一种实例路径。默认情况下,包或模块旁边的文件夹 instance 被假定为实例路径。 - `root_path`: 默认情况下,flask将自动计算引用程序根的绝对路径, 由import_name 决定. - **instance_path** 和 **instance_relative_config** 共同作用,可以改变由import_name 实例路径, 掩藏敏感配置[1](https://blog.csdn.net/f704084109/article/details/80646937#fn:3) - `static_folder `指定了静态资源的路径. 默认情况下,底层实际上是通过static_folder 确定了 static_url_path, 然后通过 self.static_url_path + /\注册的静态资源路由. - 当static_url_path 和 static_folder 同时存在时, 系统会直接使用 self.static_url_path + /\注册的静态资源路由. - static_host 和 host_matching 同时作用可以改变静态资资源存放的主机, 既可以从资源服务器读取资源. - static_url_path / static_folder / static_host / host_matching 四者结合使用可以访问资源服务器上的指定文件夹下的资源 - template_folder 设置模板文件名称 - subdomain_matching 支持子域名, 结合app.config[“SERVER_NAME”] = “域名:端口” 使用. ## Request ### 获取请求数据 #### GET请求 > request.args
1
2
3
4
5
6
7
8
9
# 请求http://127.0.0.1:8080/?user=1&pwd=1

@app.route('/')
def hello_world():
# 获取参数
args = request.args
print(args) # ImmutableMultiDict([('user', '1')])
# 是个紫癜,可以用get等方法取值
return 'Hello World!'
> request.args.getlist
1
2
3
4
5
6
7
8
9
10
# http://127.0.0.1:8080/?user=1&pwd=1&user=2

@app.route('/')
def hello_world():
# 获取对应key的多个值,type参数是回调函数
args_list = request.args.getlist('user')
args_list_type = request.args.getlist('user', type=lambda x: int(x)+1)
print(args_list_type) # [2, 3]
print(args_list) # ['1', '2']
return 'Hello World!'
#### POST请求 > request.form
1
2
3
4
5
6
7
8
9
10
# http://127.0.0.1:8080/post/
# form-data: username=2;password=1;username=3

# 处理POST请求,列表内可以添加多个值,以处理多请求类型
@app.route('/post/', methods=["POST"])
def hello_world():
# 获取POST的表单数据
data = request.form
print(data) # ImmutableMultiDict([('password', '1'), ('username', '2'), ('username', '3')])
return 'Hello World!'
> request.form.getlist
1
2
3
4
5
6
7
8
9
10
# http://127.0.0.1:8080/post/
# form-data: username=2;password=1;username=3

@app.route('/post/', methods=["POST"])
def hello_world():
data = request.form # 获取POST的表单数据
data_list = request.form.getlist("username", type=lambda x: x + "~")
print(data_list) # ['2~', '3~']
print(data) # ImmutableMultiDict([('password', '1'), ('username', '2'), ('username', '3')])
return 'Hello World!'
> request.json
1
2
3
4
5
6
7
@app.route('/json/', methods=["POST"])
def json_post():
# 获取POST的JSON数据,并自动处理为python类型数据
data = request.json
print(data) # {'a': 1, 'b': ['c', 1]}
print(type(data)) # <class 'dict'>
return 'Hello World!'
> request.file
1
2
3
4
5
6
7
8
9
10
# 请求参数(上传文件) image:A1PRO001-2020-1356.bmp

@app.route('/file', methods=["POST"])
def file_post():
file = request.files
print(file) # ImmutableMultiDict([('image', <FileStorage: 'A1PRO001-2020-1356.bmp' ('image/bmp')>)])
img = file.get("image")
# save使用相对路径总是报错,不知道为啥
img.save(r"D:\Code\LearnCodes\Python\web_Flask\flasker\static\uploads\img.png")
return 'Hello World!'
### 获取请求路径 > request.path > > request.full_path > > request.script_root > > request.url > > request.base_url > > request.url_root
1
2
3
4
5
6
7
8
9
10
# 请求http://127.0.0.1:8080/?user=1&pwd=1

@app.route('/')
def hello_world():
# 获取请求路径
path = request.path
full_path = request.full_path
print(path) # /
print(full_path) # /?user=1&pwd=1
return 'Hello World!'
***区别***: 假设您的应用程序正在以下应用程序根目录上侦听:
1
http://www.example.com/myapplication
用户请求以下URI:
1
http://www.example.com/myapplication/%CF%80/page.html?x=y
在这种情况下,上述属性的值如下: | path | `u'/π/page.html'` | | :---------- | --------------------------------------------------------- | | full_path | `u'/π/page.html?x=y'` | | script_root | `u'/myapplication'` | | base_url | `u'http://www.example.com/myapplication/π/page.html'` | | url | `u'http://www.example.com/myapplication/π/page.html?x=y'` | | url_root | `u'http://www.example.com/myapplication/'` | ## Response ### Redirect > 重定向,跳转到指定地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@app.route('/')
def hello_world():
return 'hello world'

# 跳转到test2
@app.route('/test1')
def test1():
print('this is test1')
return redirect(url_for('test2'))

# 跳转到baidu
@app.route('/test2')
def test2():
return redirect("https:www.baidu.com")

@app.route('/redirected')
def redirected():
print('重定向')
return '重定向啦!!'
### Jinja2模板引擎 > render_template 渲染模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--index.html-->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
{% if page_title %}
{{ page_title }}
{% endif %}
</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
1
2
3
4
5
6
7
8
9
<!--block.html-->
<!--继承了index.html的页面,标签block内的内容在自己的block内定义-->
{% extends "index.html" %}

{% block body %}
{% for key in user_info %}
<br>{{ key }}: {{ user_info[key] }}
{% endfor %}
{% endblock %}
1
2
3
4
5
6
7
8
9
10
@app.route('/user')
def user():
user_info = {
'name': 'letian',
'email': '[email protected]',
'age': 0,
'github': 'https://github.com/letiantian'
}
# 参数:渲染的模板文件,模板文件中的待渲染变量
return render_template('block.html', page_title='i\'s info', user_info=user_info)
### 自定义错误页面 > 处理HTTP错误,可以使用`flask.abort`函数
1
2
3
4
5
6
7
8
9
10
11
@app.route('/')
def hello_world():
return 'hello world'


@app.route('/user')
def user():
# 默认页面
abort(401) # 401 Unauthorized 未授权
# 阻塞式,不会运行下面的函数,控制台不会打印这句话
print('Unauthorized, 请先登录')
默认效果: ![image-20210207164026415](http://qiniu.rainna.xyz/image-20210207164026415.png) > 使用@app.errorhandler(状态码)来装饰一个函数用来处理对应的错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@app.route('/')
def hello_world():
return 'hello world'


@app.route('/user')
def user():
abort(401) # Unauthorized 未授权
# 阻塞式,不会运行下面的函数
print('Unauthorized, 请先登录')

# 用来处理401错误
@app.errorhandler(401)
def page_unauthorized(error):
return render_template_string('<h1> Unauthorized </h1><h2>{{ error_info }}</h2>', error_info=error), 401
- 注: 被装饰的函数返回的是一个元组,401表示状态码,缺省为200 自定义效果: ![image-20210207164409794](http://qiniu.rainna.xyz/image-20210207164409794.png) ### Cookie
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 设置cookie
@app.route('/set')
def login():
res = Response('add cookies')
res.set_cookie(key='name', value='i', expires=time.time()+6*60)
return res

# 获取cookie
@app.route('/show')
def show():
return request.cookies.__str__()

# 删除cookie
@app.route('/del')
def del_cookie():
res = Response('delete cookies')
res.set_cookie('name', '', expires=0)
return res
### Session 添加session会默认在cookie中添加sessionId字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 添加
@app.route('/do_login', methods=['POST'])
def do_login():
name = request.form.get('user_name')
session['user_name'] = name
return 'success'

# 获取session
@app.route('/show')
def show():
return session['user_name']

# 删除
@app.route('/logout')
def logout():
session.pop('user_name', None)
return redirect(url_for('login'))
## RestFul URL 将访问的url中部分作为函数参数,使得网页开发向软件开发靠拢的一种架构。 ### 转换器的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# http://127.0.0.1:8080/user/1

@app.route('/user/<username>')
def user(username):
print(username) # 1
return 'Hello World!' + str(username)


# http://127.0.0.1:8080/page/a --> 返回404,不会进入视图函数
# http://127.0.0.1:8080/page/1
# 指定类型,不符合会404
@app.route('/user/<int:page>')
def pager(page):
print(page) # 1
return 'Hello World!' + str(page)

# http://127.0.0.1:8080/page/1-3
@app.route('/page/<int:page_start>-<int:page_end>')
def pager_range(page_start, page_end):
print(page_start, page_end) # 1 3
return 'Hello World!' + str(page_start) + str(page_end)
- 注:装饰器中<>内的参数名要与视图函数形参名相同!参数个数也要对应
1
2
3
4
5
6
7
8
# 默认的转换器
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
### 自定义转换器
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
from werkzeug.routing import BaseConverter
from flask import url_for

# 自定义转换器,继承自BaseConverter
class MyConverter(BaseConverter):
def __init__(self, map_):
super(MyConverter, self).__init__(map_)
# 匹配路由,只有匹配的路由才会走to_python
self.regex = "[123]"

# url中的变量-->to_python-->函数参数
def to_python(self, value):
print("value" + value)
if value.isalpha():
value += "100"
elif value.isdigit():
value = int(value) + 100
return value

# url_for函数名反转时使用,url_for中的参数-->to_url-->真实url
def to_url(self, value):
print(value, "v")
value = str(value)
if value.isalpha():
value *= 2
elif value.isdigit():
value = int(value)*2
# 返回值必须是str类型
return str(value)


# 注册自定义转换器
app.url_map.converters['add100'] = MyConverter


# 访问http://127.0.0.1:8080/mypage/1
# 使用自定义的转换器
@app.route('/mypage/<add100:page>')
def myPager(page):
print("func" + str(page)) # func101
print(url_for("myPager", page=999)) # /mypage/1998
return 'Hello World!' + str(page)
## 路由 ### url_for > 根据``endpoint`(默认为视图函数名)反转生成url
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@app.route('/')
def hello_world():
pass

@app.route('/user/<name>')
def user(name):
pass

# endpoint给函数定义别名,默认是函数名,别名是url_for中使用
@app.route('/page/<int:num>',endpoint="myPage")
def page(num):
pass

@app.route('/test')
def test():
print(url_for('hello_world')) # /
print(url_for('user', name='i')) # /user/i
# 使用myPage而不是page,是因为通过endpoint给page赋了别名
print(url_for('myPage', num=1, q='had10%3', words="haha")) # /page/1?q=had10%253&words=haha
print(url_for('static', filename='uploads/01.jpg')) # /static/uploads/01.jpg
return 'Hello'
### 蓝图 > `蓝图`是为了将路由和视图函数分写到多个文件,相当于Django的include。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# ./user/view.py
from flask import Blueprint

# 创建一个蓝图,url_prefix用于定义url前缀,默认为"/"
user_bp = Blueprint("user", __name__, url_prefix="/user")


# 将视图函数和url映射绑定在蓝图上
@user_bp.route('/')
def user_index():
return "hello!"


@user_bp.route("/user")
def login():
return "please login"


@user_bp.route('/detail')
def detail():
return "details"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ./__init__.py
from flask import Flask

from flasker.ForBlueprint.user.views import user_bp
from flasker.config import BasicConfig


def create_app():
app = Flask(__name__)
app.config.from_object(BasicConfig)

# 在工厂函数中注册蓝图
app.register_blueprint(user_bp)
return app

### url_map > 查看当前的路由
1
2
3
4
5
6
7
8
9
10
def create_app():
app = Flask(__name__) # __name__默认指向app,即当前包名
app.config.from_object(BasicConfig)
app.register_blueprint(user_bp) # 注册蓝图
print(app.url_map)
# Map([<Rule '/detail' (GET, OPTIONS, HEAD) -> user.detail>,
# <Rule '/user' (GET, OPTIONS, HEAD) -> user.login>,
# <Rule '/' (GET, OPTIONS, HEAD) -> user.user_index>,
# <Rule '/static/<filename>' (GET, OPTIONS, HEAD) -> static>])
return app

Flask基础
http://blog.rainna.xyz/2022/10/08/2022-10-08-Flask/
作者
rainnalv
发布于
2022年10月8日
许可协议