Flask基础篇

Flask 框架

图片描述

Python 中存在众多的 Web 开发框架:Flask、Django、Tornado、Webpy 等,近几年Django 和 Flask 在 Python 领域中可以说是最主流的两个 Web 开发框架。

1. Flask 与 Django

Flask 是用 Python 语言编写轻量级的 Web 框架,较其他同类型框架更为灵活、轻便且容易上手,小型团队在短时间内就可以完成功能丰富的中小型网站或 Web 服务的实现。

Flask定位为一个轻量级的web微框架,所谓微框架指的是保持核心简单,但其具有很强的定制性,用户可以根据自己的需求来添加相应的功能

Flask框架与模块分离,可自由扩展,Flask 依赖用各种灵活的扩展(比如邮件 Flask Mail,用户认证 Flask Login,数据库 Flask SQLAlchemy)来给Web应用添加额外功能。

图片描述

Django 同样由Python 语言编写,遵循 MVC 设计模式,不过Django使用了类似于MVC的MTV设计模式 Model、Template、View ,分别代表模型、模版、视图 。

Django 是一个 Web 开发重框架。所谓‘重’指的是Django 除了实现 Web 框架的基本功能,内置了大量的模块

图片描述

与 Flask 的架构相比,两者的区别很明显:

  • Flask 仅包含最核心的框架功能,没有集成数据库访问、模板引擎等功能;
  • Django 不仅包含最核心的框架功能,还包含了数据库访问、模板引擎等功能。

其实两者最主要区别在于:Django 功能大而全,Flask 只包含基本的功能

2. 最小Flask应用

1
2
3
4
5
6
7
8
9
from flask import Flask     # 导入Flask模块
app = Flask(__name__) # 实例化Flask应用,__name__指当前模块

@app.route('/') # 返回一个装饰器,装饰器来为函数 hello_world 绑定对应的 URL
def hello_world(): # 定义函数 hello_world,访问以上 URL,触发这个函数,获取返回值。
return 'Hello World !'

if __name__ == '__main__': # 调用 run() 方法启动 Flask 应用,
app.run() # app.run(host = '0.0.0.0', port = 8888)可指定IP和端口

3. Flask路由

3.1 在 Flask 中分析 URL 参数

服务端收到客户端发送的数据后,封装形成一个请求对象,在 Flask 中,请求对象是一个模块变量 flask.request,request 对象包含了众多的属性。假设 URL 等于 http://localhost/query?name=zhan,则与 URL 参数相关的属性如下:

属性 request 获取方式 说明
url request.url http://localhost/query?name=zhan
base_url request.base_url http://localhost/query
host request.host localhost
host_url request.host_url http://localhost/
path request.path /query
full_path request.full_path /query?name=zhan
args request.args[‘name’] zhan

3.2 动态路由

一条固定的路径和一个处理函数相绑定,一条路径对应一个处理函数,无法处理复杂的情况。动态路由,路由中的路径是一个包含有参数的模板,可以匹配多条路径。

<>里的接收到的值可作为参数传递给对应的处理函数

1
2
3
4
5
@app.route('/user/<username>')                 # 默认字符串
@app.route('/post/<int:post_id>') # 整数
@app.route('/post/<float:post_id>') # 浮点数
@app.route('/post/<path:path>') # 路径字符串
@app.route('/login', methods=['GET', 'POST']) # 接收方式

3.3 Flask 的 request 对象

浏览器访问服务端,需要将相应的数据发送给服务端,可能有如下场景:

  1. 通过 URL 参数进行查询,浏览器需要将查询参数发送给服务端
  2. 提交表单 form 进行查询,浏览器需要将表单 form 中的字段发送给服务端
  3. 上传文件,浏览器需要将文件发送给服务端

服务端收到将客户端发送的数据后,封装形成一个请求对象,在 Flask 中,请求对象是一个模块变量 flask.request,它包含了如下常用属性:

属性 request获取方式 说明
method request.method 当前的请求方法
form request.form[‘name’] 表单参数及其值的字典对象
args request.args 查询字符串的字典对象
values request.values 包含所有数据的字典对象
json request.json 如果 mimetype 是 application/json,这个参数将会解析 json 数据,如果不是则返回 None
headers request.headers http 协议头部
cookies request.cookies cookie 名称和值的字典对象
files request.files 与上传文件有关的数据

4. Flask模板 -> jinja2

WEB开发中把包含变量和运算逻辑的 html 或其他格式的文本叫做模板,执行这些变量替换和逻辑计算工作的过程被称为渲染, Flask 中使用jinja2引擎完成模板渲染。

在Flask中需要建立templates 目录来存放模板,templates/index.html 是模板文件。

视图简单使用

1
2
3
4
5
6
7
8
9
from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def index():
return render_template('index.html', data = 'xxx')
# 对模板 templates/index.html 进行渲染
# data 在模板中使用{{ data }}来接收替换
app.run(debug = True)

4.1 分界符

jinja2 模板文件混合 html 语法与 jinja2 语法,使用分界符区分 html 语法与 jinja2 语法。有 4 种常见的分界符:

1
2
3
4
- {{ 变量 }},将变量放置在 {{  }} 之间;字符串、列表、字典均可在括号中取值
- {% 语句 %},将语句放置在 {% %} 之间
- {# 注释 #},将注释放置在 {# #} 之间
- \## 注释,将注释放置在 # 之后
1
2
3
4
5
6
7
8
9
10
11
## for语句
{% for book in books %}
<li>{{ book }}</li>
{% endfor %}

## if语句
{% if login %}
<p>已登录</p>
{% else %}
<p>未登录</p>
{% endif %}

4.2 测试

jinja2 提供的 tests 可以用来在语句里对变量或表达式进行测试,语法如下:

1
{% xxx is test %}

常见的 test 如下:

test 名称 功能
defined 变量是否已经定义
boolean 变量的类型是否是 boolean
integer 变量的类型是否是 integer
float 变量的类型是否是 float
string 变量是否是 string
mapping 变量的类型是否是字典
sequence 变量的类型是否是序列
even 变量是否是偶数
odd 变量是否是奇数
lower 变量是否是小写
upper 变量是否是大写

4.3 过滤器

为了方便对变量进行处理,Jinja2 提供了过滤器filter是一个函数,语法如下:

其过滤函数对输入变量进行变换处理后,返回一个值作为 Jinja2 模板的输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{{ xxx | filter }}	##  filter(xxx)

## 常见过滤器
{{ 'flask' | capitalize }} ## 首字母转成大写
{{ 'hello world' | title }} ## 每个单词的首字母都转成大写
{{ 'FLASK' | lower }} ## 字符串转小写
{{ 'flask' | upper }} ## 字符串转大写
{{ 'flask' | reverse }} ## 字符串反转
{{ 'I am %s, I am %d years old.' | format('zhan', 22) }} ## 字符串格式化
{{ ['A', 'B', 'C'] | first }} ## 获取列表第一个元素
{{ ['A', 'B', 'C'] | last }} ## 获取列表最后一个元素
{{ ['A', 'B', 'C'] | length }} ## 获取列表长度
{{ [1, 2, 3] | sum }} ## 列表求和
{{ [1, 3, 2] | sort }} ## 列表排序
{{ [1, 3, 2] | sort(reverse = True) }} ## 列表倒序排序
{{ ['I', 'LOVE', 'YOU'] | join('-') }} ## 列表转字符串并以-拼接
{{ '1 < 2' | escape }} ## 转义成 HTML
{{ name | default('zhan') }} ## 默认值
{{ 'abc' | replace('b','B') ## 字符替换

自定义过滤器

自定义过滤器的两种方法:

  • 使用 app.add_template_filter(filter_function, filter_name),将函数 filter_function 注册为名称为 filter_name 的过滤器;
  • 使用 @app.template_filter(filter_name) 装饰一个函数 filter_function,将函数 filter_function 注册为名称为 filter_name 的过滤器。

5. Flask视图

视图函数:将 URL 路径和一个函数关联,根据请求的 URL 调用对应函数处理。

视图类:将 URL 路径和一个视图类关联,根据请求的 URL 调用对应的视图类处理。

视图函数很简单,这里基于视图类展开,使用类视图好处是支持把一些共性东西放父类中,其他子类可继承

Flask.views.View 是 Flask 的标准视图类,用户定义的视图类需要继承于 Flask.views.View。使用视图类的步骤如下:

  1. 用户定义一个视图类,继承于 views.View;
  2. 在视图类中定义方法 dispatch_request,处理请求、响应客户端;
  3. 使用 app.add_url_rule (rule, view_func) 将 URL 路径和视图类绑定
1
2
3
4
5
6
7
8
9
from flask import Flask, views
app = Flask(__name__)

class Index(views.View) :
def dispatch_request(self):
return 'hello world'

app.add_url_rule(rule='/', view_func = Index.as_view('Index'))
app.run(debug = True)

视图类的本质仍是视图函数,函数 View.as_view () 返回一个视图函数,当访问页面路径 / 时,最终会调用 dispatch_request ()方法。

5.1 视图类继承

BaseView 提供了 dispatch_request 方法的实现,子类继承了这个方法,不需要重新实现 dispatch_request;BaseView 定义了两个接口函数 get_template 和 get_data,子类必须实现这两个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from flask import Flask, render_template, views
app = Flask(__name__)

# 父类
class BaseView(views.View):
def get_template(self):
raise NotImplementedError()

def get_data(self):
raise NotImplementedError()

def dispatch_request(self):
data = self.get_data()
template = self.get_template()
return render_template(template, **data)
# 子类
class UserView(BaseView):
def get_template(self):
return 'index.html'

def get_data(self):
return {
'data': 'hello world!',
}

5.2 在视图类中使用装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from functools import wraps

def check_login(func):
@wraps(func) # 使用 functools.wraps 保留原始函数的属性。
def inner(*args,**kwargs):
user = request.args.get("user")
if user and user == 'zhan':
return func(*args, **kwargs)
else:
return '请先登录'
return inner


class user(views.View):
decorators = [check_login] # 视图类添加装饰器
def dispatch_request(self):
return 'MY_DATA'

6. Flask蓝图 -> Blueprint

Flask中蓝图,提供了模块化管理程序路由的功能,使程序结构更加清晰。

Flask 的 Blueprint 具有如下属性:

  • 一个项目可以具有多个 Blueprint;
  • Blueprint 可以单独具有自己的模板、静态文件的目录;
  • 在应用初始化时,注册需要使用的 Blueprint。
1
2
3
4
5
6
7
8
9
10
11
# 主程序 app.py
from flask import Flask
import app1
import app2

app = Flask(__name__)

app.register_blueprint(app1.blueprint)
app.register_blueprint(app2.blueprint)

app.run(debug = True)

在app1和app2中,创建蓝图对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# app1.py和app2.py
from flask import Blueprint

blueprint = Blueprint('app1', __name__, url_prefix='/app1')
'''
第 1 个参数:蓝图的名称
第 2 个参数 __name__ 是该蓝图所在的模块名
第 3 个参数是指定页面的 URL 前缀为 '/app1'
'''
@blueprint.route("/index/")
def society_news():
return "app1主页"

@blueprint.route("/details/")
def society_news():
return "app1详情页"

一个完善的项目,应当

  • 程序由主程序和多个蓝图构成;
  • 每个蓝图对应一个独立的目录,存储与这个蓝图相关的文件;
  • 每个蓝图有一个独立的模板文件目录;
  • 每个蓝图有一个独立的静态文件目录。

blueprint

前端引入静态文件

1
<link rel="stylesheet" href="{{ url_for('app1.static',filename='app1.css')}}">
都看到这里了,不赏点银子吗^v^