python之flask框架详解

目录

前言

1、新建文件helloworld.py

2、相关配置参数

3.加载配置文件

3.1配置对象加载

3.2配置文件加载

3.3读取配置

4 路由定义

4.1指定路由地址

4.2 给路由传参

4.3指定请求方式

4.4 正则匹配路由

5.简单视图

5.1 返回json

5.2重定向

5.3自定义状态码

6、异常捕获

6.1 HTTP主动抛出异常

6.2捕获错误

 7、勾子函数

 8. request请求参数

9、flask上下文参数

10.cookie使用

11、Session使用

12、蓝图Blueprint

运行机制

蓝图的url前缀

注册静态路由

设置模版目录

13、Flask-Script 扩展


前言

Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展,都需要用第三方的扩展来实现,比如可以用 Flask 扩展加入ORM、窗体验证工具,文件上传、身份验证等。Flask 没有默认使用的数据库,你可以选择 MySQL,也可以用 NoSQL。

其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。

Flask常用扩展包:

  • Flask-SQLalchemy:操作数据库;
  • Flask-script:插入脚本;
  • Flask-migrate:管理迁移数据库;
  • Flask-Session:Session存储方式指定;
  • Flask-WTF:表单;
  • Flask-Mail:邮件;
  • Flask-Bable:提供国际化和本地化支持,翻译;
  • Flask-Login:认证用户状态;
  • Flask-OpenID:认证;
  • Flask-RESTful:开发REST API的工具;
  • Flask-Bootstrap:集成前端Twitter Bootstrap框架;
  • Flask-Moment:本地化日期和时间;
  • Flask-Admin:简单而可扩展的管理接口的框架
  1. 中文文档(欢迎使用 Flask — Flask 0.10.1 文档)
  2. 英文文档(http://flask.pocoo.org/docs/0.11/)
  3. 扩展列表:Extensions — Flask Documentation (2.2.x)
#安装
pip install flask==0.10.1      # 后面为版本号

1、新建文件helloworld.py

# 导入Flask类
from flask import Flask
# Flask函数接收一个参数__name__,它会指向程序所在的包

app = Flask(__name__)

# 装饰器的作用是将路由映射到视图函数 index
@app.route('/')
def index():
    return 'Hello World'

# Flask应用程序实例的 run 方法 启动 WEB 服务器

if __name__ == '__main__':
    app.run()    # 可以指定运行的主机IP地址,端口,是否开启调试模式
    # app.run(host="0.0.0.0", port=8888, debug = True)

2、相关配置参数

Flask 程序实例在创建的时候,需要默认传入当前 Flask 程序所指定的包(模块)

app = Flask(__name__)

  • import_name
    • Flask程序所在的包(模块),传 __name__ 就可以
    • 其可以决定 Flask 在访问静态文件时查找的路径
  • static_path
    • 静态文件访问路径(不推荐使用,使用 static_url_path 代替)
  • static_url_path
    • 静态文件访问路径,可以不传,默认为:/ + static_folder
  • static_folder
    • 静态文件存储的文件夹,可以不传,默认为 static
  • template_folder
    • 模板文件存储的文件夹,可以不传,默认为 templates

3.加载配置文件

在 Flask 程序运行的时候,可以给 Flask 设置相关配置,比如:配置 Debug 模式,配置数据库连接地址等等,设置 Flask 配置有以下三种方式:

  • 从配置对象中加载(常用)
    • app.config.from_object()
  • 从配置文件中加载
    • app.config.from_pyfile() 
  • 从环境变量中加载(不常用)
    • app.config.from_envvar()

3.1配置对象加载

# 配置对象,里面定义需要给 APP 添加的一系列配置
class Config(object):
    DEBUG = True


# 创建 Flask 类的对象,指向程序所在的包的名称
app = Flask(__name__)

# 从配置对象中加载配置
app.config.from_object(Config)

3.2配置文件加载

创建配置文件 config.ini,在配置文件中添加配置

格式eg: DEBUG = True

# 创建 Flask 类的对象,指向程序所在的包的名称
app = Flask(__name__)

# 从配置文件中加载配置
app.config.from_pyfile('config.ini')

3.3读取配置

  • app.config.get()
  • 在视图函数中使用 current_app.config.get()

注:Flask 应用程序将一些常用的配置设置成了应用程序对象的属性,也可以通过属性直接设置/获取某些配置:app.debug = True

4 路由定义

4.1指定路由地址

# 指定访问路径为 hello
@app.route('/hello')
def demo1():
    return 'hello world'

4.2 给路由传参

# 路由传递参数
@app.route('/user/')
def user_info(user_id):
    return 'hello %s' % user_id

# 路由传递的参数默认当做 string 处理,也可以指定参数的类型

# 路由传递参数
@app.route('/user/')
def user_info(user_id):
    return 'hello %d' % user_id

4.3指定请求方式

在 Flask 中,定义一个路由,默认的请求方式为:

  • GET
  • OPTIONS(自带)
  • HEAD(自带)
@app.route('/login', methods=['GET', 'POST'])  # 支持GET和POST,并且支持自带的OPTIONS和HEAD
def login():
    # 直接从请求中取到请求方式并返回
    return request.method

4.4 正则匹配路由

在 web 开发中,可能会出现限制用户访问规则的场景,那么这个时候就需要用到正则匹配,根据自己的规则去限定请求参数再进行访问

具体实现步骤为:

  • 导入转换器基类:在 Flask 中,所有的路由的匹配规则都是使用转换器对象进行记录
  • 自定义转换器:自定义类继承于转换器基类
  • 添加转换器到默认的转换器字典中
  • 使用自定义转换器实现自定义匹配规则

(1)导入转换器基类

from werkzeug.routing import BaseConverter

(2)自定义转换器

from flask import Flask, redirect, url_for
from werkzeug.routing import BaseConverter


class RegexConverter(BaseConverter):
    def __init__(self, url_name, *args):
        super().__init__(url_name)
        # 将接受的第1个参数当作匹配规则进行保存
        self.regex = args[0]


class ListConverter(BaseConverter):
    regex = '(\\d+,?)+\\d$'

    def to_python(self, value):
        return value.split(",")

    def to_url(self, value):
        return ",".join(str(i) for i in value)

(3)添加转换器到默认的转换器字典中,并指定转换器使用时名字为: re

app = Flask(__name__)
# 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: re
app.url_map.converters["re"] = RegexConverter
# 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: list
app.url_map.converters["list"] = ListConverter

(4)使用转换器去实现自定义匹配规则

@app.route("/demo/")
def demo1(use_name):
    return "用户名是 %s" % use_name


@app.route("/users/")
def demo11(user_list):
    return "用户列表为 %s" % user_list

# 系统自带转换器
DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}
# 系统自带的转换器具体使用方式在每种转换器的注释代码中有写,请留意每种转换器初始化的参数

自定义转换器其他两个函数实现:

继承于自定义转换器之后,还可以实现 to_python 和 to_url 这两个函数去对匹配参数做进一步处理:

  • to_python:
    • 该函数参数中的 value 值代表匹配到的值,可输出进行查看
    • 匹配完成之后,对匹配到的参数作最后一步处理再返回,比如:转成 int 类型的值再返回:
class RegexConverter(BaseConverter):
    def __init__(self, url_map, *args):
        super(RegexConverter, self).__init__(url_map)
        # 将接受的第1个参数当作匹配规则进行保存
        self.regex = args[0]

    def to_python(self, value):
        return int(value)   # 在视图函数中可以查看参数的类型,由之前默认的 str 已变成 int 类型的值

  • to_url:
    • 在使用 url_for 去获取视图函数所对应的 url 的时候,会调用此方法对 url_for 后面传入的视图函数参数做进一步处理
    • 具体可参见 Flask 的 app.py 中写的示例代码:ListConverter

5.简单视图

5.1 返回json

在使用 Flask 给客户端返回 JSON 数据时,可以直接使用 jsonify 生成一个 JSON 的响应

# 返回JSON
@app.route('/demo')
def demo():
    json_dict = {
        "user_id": 10,
        "user_name": "laowang"
    }
    return jsonify(json_dict)
注:不推荐使用 json.dumps 转成 JSON 字符串直接返回,因为返回的数据要符合 HTTP 协议规范,如果是 JSON 需要指定 content-type:application/json

5.2重定向

(1)重定向到百度

# 重定向
@app.route('/demo')
def demo():
    return redirect('http://www.baidu.com')

(2)重定向到自己写的视图函数

  • 直接填写自己 url 路径
  • 使用 url_for 生成指定视图函数所对应的 url
@app.route('/demo1')
def demo1():
    return 'demo1'

# 重定向,采用url_for生成demo1对应的url
@app.route('/demo2')
def demo2():
    return redirect(url_for('demo1'))

(3)重定向到带有参数的视图函数

# 路由传递参数
@app.route('/user/')
def user_info(user_id):
    return 'hello %d' % user_id

# 重定向,在url_for中传入参数
@app.route('/demo5')
def demo5():
    # 使用 url_for 生成指定视图函数所对应的 url
    return redirect(url_for('user_info', user_id=100))

5.3自定义状态码

自定义不符合 http 协议的状态码

@app.route('/demo')
def demo():
    return '状态码为 666', 666

6、异常捕获

6.1 HTTP主动抛出异常

  • abort 方法
    • 抛出一个给定状态代码的 HTTPException 或者 指定响应,例如想要用一个页面未找到异常来终止请求,你可以调用 abort(404)。
  • 参数:
    • code – HTTP的错误状态码
# abort(404)
# 抛出状态码的话,只能抛出 HTTP 协议的错误状态码
abort(500)

6.2捕获错误

  • errorhandler 装饰器
    • 注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法
  • 参数:
    • code_or_exception – HTTP的错误状态码或指定异常

例如统一处理状态码为500的错误给用户友好的提示:

# 处理所有500类型的异常

@app.errorhandler(500)
def internal_server_error(e):
    return '服务器搬家了'


# 处理特定的异常项
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
    return '除数不能为0'

 7、勾子函数

在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:

  • 在请求开始时,建立数据库连接;
  • 在请求开始时,根据需求进行权限校验;
  • 在请求结束时,指定数据的交互格式;

为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。

请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:

  • before_first_request
    • 在处理第一个请求前执行
  • before_request
    • 在每次请求前执行
    • 如果在某修饰的函数中返回了一个响应,视图函数将不再被调用
  • after_request
    • 如果没有抛出错误,在每次请求后执行
    • 接受一个参数:视图函数作出的响应
    • 在此函数中可以对响应值在返回之前做最后一步修改处理
    • 需要将参数中的响应在此参数中进行返回
  • teardown_request:
    • 在每次请求后执行
    • 接受一个参数:错误信息,如果有相关错误抛出
from flask import Flask
from flask import abort

app = Flask(__name__)


# 在第一次请求之前调用,可以在此方法内部做一些初始化操作
@app.before_first_request
def before_first_request():
    print("before_first_request")


# 在每一次请求之前调用,这时候已经有请求了,可能在这个方法里面做请求的校验
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数
@app.before_request
def before_request():
    print("before_request")
    # if 请求不符合条件:
    #     return "laowang"


# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@app.after_request
def after_request(response):
    print("after_request")
    response.headers["Content-Type"] = "application/json"
    return response


# 请每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(e):
    print("teardown_request")


@app.route('/')
def index():
    return 'index'

if __name__ == '__main__':
    app.run(debug=True)

执行结果如下:

在第1次请求时的打印:
before_first_request
before_request
after_request
teardown_request

在第2次请求时的打印:
before_request
after_request
teardown_request

 8. request请求参数

  request 就是flask中代表当前请求的 request 对象,作为请求上下文变量(理解成全局变量,在视图函数中直接使用可以取到当前本次请求)

  常用属性:

属性 说明 类型
data 记录请求的数据,并转换为字符串 *
form 记录请求中的表单数据 MultiDict
args 记录请求中的查询参数 MultiDict
cookies 记录请求中的cookie信息 Dict
headers 记录请求中的报文头 EnvironHeaders
method 记录请求使用的HTTP方法 GET/POST
url 记录请求的URL地址 string
files 记录请求上传的文件 *

# 获取上传的图片并保存到本地
@app.route('/', methods=['POST'])
def index():
    pic = request.files.get('pic')
    pic.save('./static/aaa.png')
    return 'index'

9、flask上下文参数

  上下文:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。

  Flask中有两种上下文,请求上下文(request context)和应用上下文(application context)

  1. application 指的就是当你调用app = Flask(__name__)创建的这个对象app

  2. request 指的是每次http请求发生时,WSGI server(比如gunicorn)调用Flask.__call__()之后,在Flask对象内部创建的Request对象;

  3. application 表示用于响应WSGI请求的应用本身,request 表示每次http请求;

  4. application的生命周期大于request,一个application存活期间,可能发生多次http请求,所以,也就会有多个request

  源码了解一下 flask 如何实现这两种context: 

# 代码摘选自flask 0.5 中的ctx.py文件, 进行了部分删减
class _RequestContext(object):
    
    def __init__(self, app, environ):
        self.app = app
        self.request = app.request_class(environ)
        self.session = app.open_session(self.request)
        self.g = _RequestGlobals()

# flask 使用_RequestContext的代码如下:
class Flask(object):

    def request_context(self, environ):
        return _RequestContext(self, environ)

Flask类中,每次请求都会调用这个request_context函数。这个函数则会创建一个_RequestContext对象。这个对象在创建时,将Flask实例的本身作为实参传入_RequestContext自身,因此,self.app = Flask()

所以,虽然每次http请求都会创建一个_RequestContext对象,但是,每次创建的时候都会将同一个Flask对象传入该对象的app成员变量,使得:

由同一个Flask对象响应的请求所创建的_RequestContext对象的app成员变量都共享同一个application

通过在Flask对象中创建_RequestContext对象,并将Flask自身作为参数传入_RequestContext对象的方式,实现了多个request context对应一个application context 的目的。  

(1)请求上下文(request context)

  请求上下文对象有:request、session

  • request

封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user'),获取的是get请求的参数。

  • session

用来记录请求会话中的信息,针对的是用户信息。举例:session['name'] = user.id,可以记录用户信息。还可以通过session.get('name')获取用户信息。

(2)应用上下文(application context)

  应用上下文对象有:current_app,g

  • current_app

  应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:

  1. 应用的启动脚本是哪个文件,启动时指定了哪些参数
  2. 加载了哪些配置文件,导入了哪些配置
  3. 连了哪个数据库
  4. 有哪些public的工具类、常量
  5. 应用跑再哪个机器上,IP多少,内存多大
current_app.name
current_app.test_value='value'
  • g变量

  g 作为 flask 程序全局的一个临时变量,充当中间媒介的作用,我们可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别 

g.name='zhangsan'
  1. 请求上下文:保存了客户端和服务器交互的数据
  2. 应用上下文:flask 应用程序运行过程中,保存的一些配置信息,比如程序名、数据库连接、应用信息等

10.cookie使用

Cookie:指某些网站为了辨别用户身份、进行会话跟踪而储存在用户本地的数据(通常经过加密)。

  • 复数形式Cookies。
  • Cookie是由服务器端生成,发送给客户端浏览器,浏览器会将Cookie的key/value保存,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。
  • Cookie的key/value可以由服务器端自己定义。
# 设置cookie
# 当浏览器请求某网站时,会将本网站下所有Cookie信息提交给服务器,所以在request中可以读取Cookie信息

from flask imoprt Flask, make_response
@app.route('/cookie')
def set_cookie():
    resp = make_response('this is to set cookie')
    resp.set_cookie('username', 'zhangsan')
    resp.set_cookie("pwd", "12321")     
    return resp

# 设置过期时间
@app.route('/cookie')
def set_cookie():
    response = make_response('hello world')
    response.set_cookie('username', 'zhangsan', max_age=3600)  # 过期时间为3600秒
    return response


# 获取cookie
from flask import Flask, request

@app.route('/request')
def resp_cookie():
    resp = request.cookies.get('username')
    return resp

# 删除cookie
@app.route("/logout")
def logout():
    resp = make_response("success")
    resp.delete_cookie("username")
    resp.delete_cookie("pwd")
    return resp

11、Session使用

# session数据的获取
# session:请求上下文对象,用于处理http请求中的一些数据内容

# 设置secret_key
# 作用:设置一个secret_key值,用作各种 HASH
app.secret_key = 'python'
# 考虑到安全性, 这个密钥不建议存储在程序中. 最好的方法是存储在你的系统环境变量中, 通过 os.getenv(key, default=None) 获得.
    

#设置session,并重定向到获取session的index函数
@app.route("/login")
def login():
    session["username"] = 'zhangsan'
    session['password'] = "1234321"
    return redirect(url_for("index"))


# 获取session
@app.route("/")
def index():
    username = session.get("username")
    password = session.get("password")
    return "世界真美好%s======%s" % (username, password)


# 删除session,并重定向到获取session的index函数
@app.route("/logout")
def logout():
    session.pop("username")
    session.pop("password")
    return redirect(url_for("index"))

12、蓝图Blueprint

Blueprint 是一个存储操作方法的容器,这些操作在这个Blueprint 被注册到一个应用之后就可以被调用,Flask 可以通过Blueprint来组织URL以及处理请求。

Flask使用Blueprint让应用实现模块化,在Flask中,Blueprint具有如下属性:

  • 一个应用可以具有多个Blueprint
  • 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/”、“/sample”或者子域名
  • 在一个应用中,一个模块可以注册多次
  • Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
  • 在一个应用初始化时,就应该要注册需要使用的Blueprint

但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。

蓝图/Blueprint对象用起来和一个应用/Flask对象差不多,最大的区别在于一个 蓝图对象没有办法独立运行,必须将它注册到一个应用对象上才能生效

使用蓝图可以分为三个步骤

  • 1,创建一个蓝图对象
admin=Blueprint('admin',__name__)
  • 2,在这个蓝图对象上进行操作,注册路由,指定静态文件夹,注册模版过滤器
@admin.route('/admin')
def index():
    return 'admin_home'
  • 3,在应用对象上注册这个蓝图对象
app.register_blueprint(admin,url
_prefix='/admin')

当这个应用启动后,通过/admin/admin/可以访问到蓝图中定义的视图函数

运行机制

  • 蓝图是保存了一组将来可以在应用对象上执行的操作,注册路由就是一种操作
  • 当在应用对象上调用 route 装饰器注册路由时,这个操作将修改对象的url_map路由表
  • 然而,蓝图对象根本没有路由表,当我们在蓝图对象上调用route装饰器注册路由时,它只是在内部的一个延迟操作记录列表defered_functions中添加了一个项
  • 当执行应用对象的 register_blueprint() 方法时,应用对象将从蓝图对象的 defered_functions 列表中取出每一项,并以自身作为参数执行该匿名函数,即调用应用对象的 add_url_rule() 方法,这将真正的修改应用对象的路由表

蓝图的url前缀

  • 当我们在应用对象上注册一个蓝图时,可以指定一个url_prefix关键字参数(这个参数默认是/)
  • 在应用最终的路由表 url_map中,在蓝图上注册的路由URL自动被加上了这个前缀,这个可以保证在多个蓝图中使用相同的URL规则而不会最终引起冲突,只要在注册蓝图时将不同的蓝图挂接到不同的自路径即可

  • url_for

url_for('admin.index') # /admin/admin/   将admin下index函数路由注册   

注册静态路由

和应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在 创建时指定 static_folder 参数。

下面的示例将蓝图所在目录下的static_admin目录设置为静态目录

admin = Blueprint("admin",__name__,static_folder='static_admin')
app.register_blueprint(admin,url_prefix='/admin')

现在就可以使用/admin/static_admin/ 访问static_admin目录下的静态文件了 定制静态目录URL规则 :可以在创建蓝图对象时使用 static_url_path 来改变静态目录的路由。下面的示例将为 static_admin 文件夹的路由设置为 /lib

admin = Blueprint("admin",__name__,static_folder='static_admin',static_url_path='/lib')
app.register_blueprint(admin,url_prefix='/admin')

设置模版目录

蓝图对象默认的模板目录为系统的模版目录,可以在创建蓝图对象时使用 template_folder 关键字参数设置模板目录

admin = Blueprint('admin',__name__,template_folder='my_templates')

注:如果在 templates 中存在和 my_templates 同名文件,则系统会优先使用 templates 中的文件,参考链接:python - flask blueprint template folder - Stack Overflow

13、Flask-Script 扩展

通过使用Flask-Script扩展,我们可以在Flask服务器启动的时候,通过命令行的方式传入参数。而不仅仅通过app.run()方法中传参,比如我们可以通过:

python main.py runserver -host ip地址 -P 端口

# 1 、安装 Flask-Script 扩展
pip install flask-script


# 2、集成 Flask-Script
from flask import Flask
from flask_script import Manager

app = Flask(__name__)
# 把 Manager 类和应用程序实例进行关联
manager = Manager(app)

@app.route('/')
def index():
    return 'hello world'

if __name__ == "__main__":
    manager.run()

你可能感兴趣的:(flask,flask,python,后端)