flask 之(五) --- 对象|钩子|拆分

内置对象

  • request:  请求的所有信息
  • session     服务端会话技术的接口
  • config:      当前项目的配置信息,模板中可以直接使用
  • g:global  在单次请求过程中,实现全局数据共享(可以帮助开发者实现跨函数传递数据)
from flask import Blueprint, render_template, g
from .models import User

user_blue = Blueprint('user_blue',__name__,url_prefix="/users/")

@user_blue.route('/')
def hello_world():
    return 'User Index!'

@user_blue.route('/create/')
def create():
    # g的信息即使没有被返回渲染,也可以在html中获取到
    g.msg = "g的信息"
    return render_template('index.html')
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Index</title>
 6 </head>
 7 <body> # 获取全局变量的值
 8 <h1>{{ g.msg }}</h1>
 9 </body>
10 </html>
  • config 或者app 

  config表示当前运行的项目。遍历config时是获取当前运行的App的配置 [配置应该是有价值的]

  在python代码中current_app.config,当前运行的app。用在函数中,使用记得是在初始化完成之后

1 <body>
2 <ul>
3     {% for foo in config %}
4         <li>{{ foo }}</li>
5     {% endfor %}
6 </ul>
7 </body>

钩子函数

编程模型 :

  • OOP:   面向对象编程;POP:   面向过程编程
  • AOP: 面向切面编程;IOP:  面向接口编程

面向切面: 

  在不修改原有逻辑代码的基础上。动态的去添加一些功能或者控制逻辑(装饰器类似)

  动态介入到既有流程中,去监控或者去获取我们想要的数据

  蓝图和app上都有钩子函数,app上的钩子优先级更高一些;蓝图只能处理本蓝图内容的信息

  

重要概念:

  切面:切开后可以获取的东西叫切面。(在flask中叫做钩子函数)

     请求前切面获取:  request

     请求后切面获取:  request、response

     请求异常切面获取: exception、request

  切点:可切的地方叫切点。请求前请求后请求异常

     请求前切点:  @user_blue.before_request

     请求后切点:  @user_blue.before_request

     请求异常切点: @user_blue.errorhandler(500)

 1 """
 2 常用请求钩子 3 
 4     before_first_request:     在处理app第一个请求前运行。
 5 
 6     before_request:        在每次请求前运行。
 7 
 8     after_request:        如果处理逻辑没有异常抛出,在每次请求后运行。
 9 
10     teardown_request:       在每次请求后运行,即使处理发生了错误。
11 
12     teardown_appcontext:     在应用上下文从栈中弹出之前运行
13 """
 1 # 定义钩子函数:相当于django中的中间件
 2 
 3 # before_request装饰的方法会加载到app的before_request_funcs列表中,按加载的顺序依次执行,不需要参数
 4 @app.before_request
 5 def test_before_request():
 6     print('before_request:2')
 7 
 8 # before_first_request装饰的函数加载到before_first_request_funcs列表中,只不过在app第一次接收到请求后执行,其他时候不再执行
 9 @app.before_first_request
10 def test_before_first_request():
11     print('before_first_request:1')
12 
13 # after_request装饰的函数加载到after_request_funcs列表中,传入的参数是response对象,可以对其进行拦截修改,必须返回一个response对象
14 @app.after_request
15 def test_after_request(resp):
16     print(resp)
17     print('after_request:3')
18     return resp
 1 @bp.before_request
 2 def test_before_request():
 3     """
 4     每个视图函数执行之前,都会执行此函数
 5     根据业务决定是否有返回值
 6     :return:
 7     """
 8     print('in before request')
 9     g.start_time = time.time()
10 
11     # before_request 可以用在对所有请求的登录认证中
12     # 1、通过session,获取用户id
13     # 2、根据用户id查找用户对象
14     # 3、如果用户id合法,则认为登录合法
15     # 4、在后续的所有视图函数中,可以直接使用 g.user 获得当前登录的user对象
16     # uid = session.get('uid')
17     # user = User.query.get(uid)
18     #
19     # if user:
20     #     g.user = user
21     #     g.is_login = True
22     # else:
23     #     g.user = None
24     #     g.is_login = False
25 
26 
27 
28 @bp.after_request
29 def test_after_request(response):
30     print('in after request')
31     start_time = g.start_time
32     exec_time = time.time() - start_time
33     print('exec_time:', exec_time)
34     return response
钩子和对象
  • 内置蓝图钩子 [请求前] 
 1 from flask import Blueprint, render_template, g
 2 from .models import User
 3 
 4 user_blue = Blueprint('user_blue',__name__,url_prefix="/users/")
 5 
 6 @user_blue.route('/')
 7 def hello_world():
 8     return 'User Index!'
 9 
10 @user_blue.route('/create/')
11 def create():
12     
13     print('create视图函数,在钩子函数执行之后执行')
14     
15     # 接收before钩子函数传递过来的数据
16     print("before钩子函数传递过来的数据:",g.data)
17     
18     # g的信息即使没有被返回渲染,也可以在html中获取到
19     g.msg = "g的信息"
20     return render_template('index.html')
21 
22 # 切面 即钩子函数
23 @user_blue.before_request
24 def before():
25     
26     print("before钩子函数,在路由函数执行之前执行")
27     
28     # 向create视图函数中传递数据
29     g.data = "钩子函数 给 视图函数 的数据"
  • 内置蓝图钩子 [请求后]
 1 from flask import Blueprint
 2 from .models import Movie
 3 
 4 movie_blue = Blueprint("movie_blue",__name__,url_prefix="/movies/")
 5 
 6 @movie_blue.route('/')
 7 def index():
 8     return "Movie Index"
 9 
10 @movie_blue.before_request
11 def before():
12     print("before")
13 
14 @movie_blue.after_request
15 def after(resp):  # 切开时获取到的返回值作为参数
16     print("after")
17              # 切开后,需要把切之前要返回的信息接着返回出去
18     return resp 
  • 内置蓝图钩子 [异常捕获]
 1 from flask import Blueprint
 2 from .models import Movie
 3 
 4 movie_blue = Blueprint("movie_blue",__name__,url_prefix="/movies/")
 5 
 6 @movie_blue.route('/')
 7 def index():
 8     return "Movie Index"
 9 # 请求前
10 @movie_blue.before_request
11 def before():
12     print("before")
13 # 请求后
14 @movie_blue.after_request
15 def after(resp):
16     print("after")
17     # 切开后,需要把切之前要返回的信息接着返回出去
18     return resp
19 # 异常捕获
20 @movie_blue.errorhandler(500)   # 错误码
21 def error500(exce):         # 获取异常参数
22     print(type(exce))
23     return "异常信息屏蔽,返回到首页"

  可以在项目的目录下新建一个 middleware.py 文件。将切面编程代码编写在middleware文件中。

  然后在__init__.py文件中把中间件middleware加载进来:load_middleware(app)。改成全局的中间件,优先级更高

  在middleware.py 文件中,声明一个 load_mindleware(app)函数,导入相应的包,然后在此函数下写中间件视图函数

1 def load_middleware(app):
2 
3     @app.before_request()
4     def before():
5         print("before"

项目拆分 

  在开发过程中多人合作完成,就需要考虑项目的重构性、扩展性。在一个文件中写代码,是非常不理想的。就需要对文件进行拆分。

  拆分:

    首先有一个文件的程序,名字就叫这个项目的名字。通过flask-script可以将这个文件改变成统筹管理这个项目的文件,

    将此文件名字改成 manage。用它来统筹管理我们这个项目。然后新建一个名为App包文件,用来存放此项目的文件

    在包文件中,通常将创建flask的过程写在 __init__.py 文件中,天然单例首先被调用。

    在setting.py文件中做配置信息,在__init__ 中加载配置文件进来。

    在extension.py文件中做扩展库的信息,在__init__中初始化扩展库。

    在views.py文件中做视图函数信息,在__init__中初始化路由,尽量最后初始化路由(是web的入口,在启动时需要其他信息加载好)

    在model.py文件中操作数据信息,views.py 文件操作 model.py ;model.py 还需要extenson扩展库文件中的db

  总结:

     配置要优先于初始化。首先初始化 配置信息;然后初始化 扩展库;最后初始化 路由。启动项目的时候从manage.py开始启动 

     启动的时候首先创建Flask对象,然后对Flask对象的配置进行各种初始化,加载完初始化后去加载扩展库,最后初始化路由。 

  分析:

    manager.py文件中创建Flask的app实例(对象)。通过App/__init__.py文件中的 create_app 函数来创建Flask的App实例。

      并且把创建好的app实例交给Manager来管理 

    App/__init__.py文件中加载初始化各种配置:创建app实例、加载配置文件、加载扩展库文件、加载路由(蓝图)。

      创建app实例:通过 create_app 函数来创建Flask的app实例(对象)。

      加载配置文件:通过 config.from_object(Config) 加载对象的方式加载setting.py中创建的配置类Config对象

      加载扩展文件:通过 init_ext(app) 函调用的方式,将app实例以参数的形式传入到扩展文件extension.py文件中的init_ext函数中。

             对app实例进行扩展信息初始化。通过这种方式避免循环导入的问题

      加载路由文件:通过 init_blue(app) 函数调用的方式,将app实例以参数的形式传入到视图views.py文件中。 在init_blue函数中,对app进行(蓝图)初始化。

             避免循环导入问题的出现

  坑点:

    在使用views文件中的视图函数创建数据库表的时候,需要将models文件中的表导入到视图文件中。

    即:views.py 文件中导入:from App.models import User。不然无法识别需要创建的该表的信息。

    

  •  manage.py 文件
 1 """ manage.py """
 2 
 3 import os
 4 from flask_script import Manager
 5 from App import create_app
 6 
 7 # 从系统环境中获取参数FLASK_ENV的值给env,判断是什么环境下的服务器
 8 # 避免外人修改代码,方便代码放在什么环境服务器下,就在什么环境下运行
 9 # 在系统终端 vim .bashrc 编写系统环境变量:#FLASK_ENV。export FLASK_ENV = "develop" 保存退出
10 env = os.environ.get("FLASK_ENV") or 'default'
11 
12 # 首先创建一个flask对象、加载配置、加载扩展库、初始化路由
13 app = create_app(env)
14 # flask-scripy扩展
15 manager = Manager(app)
16 
17 if __name__ == '__main__':
18     manager.run()
  • __init__.py
 1 ''' __init__.py '''
 2 from flask import Flask
 3 from App.settings import Config, envs
 4 from App.extension import init_ext
 5 from App.views import init_blue
 6 
 7 def create_app(env):
 8 
 9     # 创建Flask对象
10     app = Flask(__name__)
11 
12     # 加载初始化配置。 从类对象中加载。env参数确定在什么环境下的
13     app.config.from_object(envs.get(env))
14 
15     # 加载初始化扩展库。通过懒加载的方式加载(调用函数时参数的传递)
16     init_ext(app)
17 
18     # 加载初始化路由器。通过懒加载的方式加载(调用函数时参数的传递)
19     init_blue(app)
20 
21     return app
  • settings.py
 1 ''' settings.py '''
 2 
 3 # 对连接数据库的配置信息进行格式化整理
 4 def get_db_uri(dbinfo):
 5     database = dbinfo.get("DATABASE")
 6     driver = dbinfo.get("DRIVER")
 7     user = dbinfo.get("USER")
 8     password = dbinfo.get("PASSWORD")
 9     host = dbinfo.get("HOST")
10     port = dbinfo.get("PORT")
11     name = dbinfo.get("NAME")
12     return "{}+{}://{}:{}@{}:{}/{}".format(database,driver,user,password,host,port,name)
13 
14 
15 # 配置信息初始化
16 class Config:
17     SECRET_KEY = 'asdfghjjkl'  # 随机值。系统可以基于一个密码随机生成随机值,这个值可以用来做密钥:
18     DEBUG = False
19     TESTING = False
20     SQLALCHEMY_TRACK_MODIFICATIONS = False
21 
22 
23 # 开发环境
24 class DevelopConfig(Config):
25     DEBUG = True
26     # 数据库信息配置
27     dbinfo = {
28         "DATABASE": "mysql",
29         "DRIVER":"pymysql",
30         "USER": "root",
31         "PASSWORD": "guoyapeng",
32         "HOST": "localhost",
33         "PORT": "3306",
34         "NAME": "FlaskModel",
35     }
36     SQLALCHEMY_DATABASE_URI = get_db_uri(dbinfo)
37 # 测试环境
38 class TestingConfig(Config):
39     TESTING = True
40     # 数据库信息配置
41     dbinfo = {
42         "DATABASE": "mysql",
43         "DRIVER":"pymysql",
44         "USER": "root",
45         "PASSWORD": "guoyapeng",
46         "HOST": "localhost",
47         "PORT": "3306",
48         "NAME": "FlaskModel",
49     }
50     SQLALCHEMY_DATABASE_URI = get_db_uri(dbinfo)
51 # 演示环境
52 class StagingConfig(Config):
53     # 数据库信息配置
54     dbinfo = {
55         "DATABASE": "mysql",
56         "DRIVER":"pymysql",
57         "USER": "root",
58         "PASSWORD": "guoyapeng",
59         "HOST": "localhost",
60         "PORT": "3306",
61         "NAME": "FlaskProject",
62     }
63     SQLALCHEMY_DATABASE_URI = get_db_uri(dbinfo)
64 # 生产环境
65 class ProductConfig(Config):
66     # 数据库信息配置
67     dbinfo = {
68         "DATABASE": "mysql",
69         "DRIVER":"pymysql",
70         "USER": "root",
71         "PASSWORD": "guoyapeng",
72         "HOST": "localhost",
73         "PORT": "3306",
74         "NAME": "FlaskModel",
75     }
76     SQLALCHEMY_DATABASE_URI = get_db_uri(dbinfo)
77 
78 # 获取在什么环境下,不同环境下数据库不同
79 envs = {
80     "develop":  DevelopConfig,
81     "texting":  TestingConfig,
82     "staging":  StagingConfig,
83     "product":  ProductConfig,
84     "default":  ProductConfig,
85 }
  • extension.py
 1 ''' extension.py '''
 2 from flask_sqlalchemy import SQLAlchemy
 3 from flask_migrate import Migrate
 4 
 5 db = SQLAlchemy()
 6 migate  = Migrate()
 7 
 8 # 通过写一个函数,使用函数传参数的形式将app传递过来。然后db利用app进行初始化
 9 def init_ext(app):
10     db.init_app(app) # db需要使用传过来的参数app来进行初始化
11     migate.init_app(app,db)
  • models.py
1 ''' models.py '''
2 from App.extension import db
3 
4 class User(db.Model):
5     id = db.Column(db.Integer,primary_key=True,autoincrement=True)
6     name = db.Column(db.String(32),unique=True)
7     password = db.Column(db.String(32))
  • views.py 
 1 ''' views.py '''
 2 from flask import Blueprint
 3 from App.extension import db
 4 from App.models import User
 5 
 6 blue = Blueprint('blue',__name__)
 7 def init_blue(app):
 8     app.register_blueprint(blueprint=blue)
 9 
10 
11 @blue.route('/')
12 def hello_world():
13     return 'Hello index!'
14 
15 @blue.route('/create/')
16 def create():
17     db.create_all()
18     return '创建成功'

 视图拆分

  选中views.py文件,右击选择Refactor选项,然后选择Convert To Python Package选项。将原来的views.py文件转换成一个包文件。再在这个包文件中去新建需要的view视图文件。

  • __init.py__
1 from App.views.movie_view import movie_blue
2 from App.views.user_view import user_blue
3 
4 # 蓝图注册初始化
5 def init_blue(app):
6     app.register_blueprint(blueprint=user_blue)
7     app.register_blueprint(blueprint=movie_blue)
  • user_view.py
 1 from flask import Blueprint
 2 from App.extension import db
 3 from App.models import User
 4 
 5 user_blue = Blueprint('user_blue',__name__,url_prefix="/users/")
 6 
 7 @user_blue.route('/')
 8 def hello_world():
 9     return 'User Index!'
10 
11 @user_blue.route('/create/')
12 def create():
13     db.create_all()
14     return '创建成功'
  • movie_view.py
1 from flask import Blueprint
2 
3 movie_blue = Blueprint("movie_blue",__name__,url_prefix="/movies/")
4 
5 @movie_blue.route('/')
6 def index():
7     return "Movie Index"

模型拆分

  选中model.py文件,右击选择Refactor选项,然后选择Convert To Python Package选项。

  将原来的model.py文件转换成一个包文件。再在这个包文件中去新建需要的model模型文件。

  • __init.py__
# 将各model文件导入到此文件中,作为对外的统一接口设置。
from .user_model import User
from .movie_model import Movie
  • user_model.py
1 from App.extension import db
2 
3 class User(db.Model):
4     id = db.Column(db.Integer,primary_key=True,autoincrement=True)
5     name = db.Column(db.String(32),unique=True)
6     password = db.Column(db.String(32))
  • movie_model.py
1 from App.extension import db
2 
3 class Movie(db.Model):
4     id = db.Column(db.Integer, primary_key=True, autoincrement=True)
5     m_name = db.Column(db.String(32))
6     m_detail = db.Column(db.String(32))

高级拆分方法 [推荐使用]

  此方法推荐使用。将每一个功能模块拆分一个包文件,然后再在这个包文件中进行路由视图函数、模型的逻辑书写。今后使用直接copy后,根据需求添加修改就可以

  day05_chaiAdvanced包文件中 是此项目的各种配置。以后想加什么功能模块,就在此文件同级目录创建包文件模块,然后在 工程 目录下的views.py文件中注册上就可以了。 

  

  第一块是 程序的入口,叫做:manage。

  第二块是 和工程名同名的一个包,里面有__init__.py文件、setting.py文件配置信息、extension.py文件负责扩展库、views.py文件负责路由注册

    __init__.py 文件中,有一个创建app设置,在程序入口manage内调用,

    __init__内 : 根据设置文件setting.py 加载配置;

          根据扩展库文件extension.py 初始化第三方扩展库;

          根据初始化路由文件views.py 进行加载路由

  第三块是 各个子模块。user包文件、movies包文件.........。各个子模块中又都有自己的 views.py文件和models.py文件

manage.py 文件中

 1 """ manage.py """
 2 
 3 import os
 4 
 5 from flask_migrate import MigrateCommand
 6 from flask_script import Manager
 7 from day05_chaiAdvanced import create_app
 8 
 9 # 从系统环境中获取参数FLASK_ENV的值给env,判断是什么环境下的服务器
10 # 避免外人修改代码,方便代码放在什么环境服务器下,就在什么环境下运行
11 # 在系统终端 vim .bashrc 编写系统环境变量:#FLASK_ENV。export FLASK_ENV = "develop" 保存退出
12 env = os.environ.get("FLASK_ENV") or 'default'
13 
14 # 首先创建一个flask对象、加载配置、加载扩展库、初始化路由
15 app = create_app(env)
16 # flask-scripy扩展
17 manager = Manager(app)
18 manager.add_command("db",MigrateCommand)
19 
20 if __name__ == '__main__':
21     manager.run()

day05_chaiAdvanced 包文件中

  • __init__.py
 1 ''' __init__.py '''
 2 from flask import Flask
 3 from day05_chaiAdvanced.settings import Config, envs
 4 from day05_chaiAdvanced.extension import init_ext
 5 from day05_chaiAdvanced.views import init_blue
 6 
 7 def create_app(env):
 8 
 9     # 创建Flask对象
10     app = Flask(__name__)
11 
12     # 加载初始化配置。 从类对象中加载。env参数确定在什么环境下的
13     app.config.from_object(envs.get(env))
14 
15     # 加载初始化扩展库。通过懒加载的方式加载(调用函数时参数的传递)
16     init_ext(app)
17 
18     # 加载初始化路由器。通过懒加载的方式加载(调用函数时参数的传递)
19     init_blue(app)
20 
21     return app
  • extension.py
 1 ''' extension.py '''
 2 from flask_sqlalchemy import SQLAlchemy
 3 from flask_migrate import Migrate
 4 
 5 db = SQLAlchemy()
 6 migate  = Migrate()
 7 
 8 # 通过写一个函数,使用函数传参数的形式将app传递过来。然后db利用app进行初始化
 9 def init_ext(app):
10     db.init_app(app) # db需要使用传过来的参数app来进行初始化
11     migate.init_app(app,db)
  • settings.py
 1 ''' settings.py '''
 2 # 对连接数据库的配置信息进行格式化整理
 3 def get_db_uri(dbinfo):
 4     database = dbinfo.get("DATABASE")
 5     driver = dbinfo.get("DRIVER")
 6     user = dbinfo.get("USER")
 7     password = dbinfo.get("PASSWORD")
 8     host = dbinfo.get("HOST")
 9     port = dbinfo.get("PORT")
10     name = dbinfo.get("NAME")
11     return "{}+{}://{}:{}@{}:{}/{}".format(database,driver,user,password,host,port,name)
12 
13 
14 # 配置信息初始化
15 class Config:
16     SECRET_KEY = 'asdfghjjkl'  # 随机值。系统可以基于一个密码随机生成随机值,这个值可以用来做密钥:
17     DEBUG = False
18     TESTING = False
19     SQLALCHEMY_TRACK_MODIFICATIONS = False
20 
21 
22 # 开发环境
23 class DevelopConfig(Config):
24     DEBUG = True
25     # 数据库信息配置
26     dbinfo = {
27         "DATABASE": "mysql",
28         "DRIVER":"pymysql",
29         "USER": "root",
30         "PASSWORD": "guoyapeng",
31         "HOST": "localhost",
32         "PORT": "3306",
33         "NAME": "FlaskModel",
34     }
35     SQLALCHEMY_DATABASE_URI = get_db_uri(dbinfo)
36 # 测试环境
37 class TestingConfig(Config):
38     TESTING = True
39     # 数据库信息配置
40     dbinfo = {
41         "DATABASE": "mysql",
42         "DRIVER":"pymysql",
43         "USER": "root",
44         "PASSWORD": "guoyapeng",
45         "HOST": "localhost",
46         "PORT": "3306",
47         "NAME": "FlaskModel",
48     }
49     SQLALCHEMY_DATABASE_URI = get_db_uri(dbinfo)
50 # 演示环境
51 class StagingConfig(Config):
52     # 数据库信息配置
53     dbinfo = {
54         "DATABASE": "mysql",
55         "DRIVER":"pymysql",
56         "USER": "root",
57         "PASSWORD": "guoyapeng",
58         "HOST": "localhost",
59         "PORT": "3306",
60         "NAME": "FlaskProject",
61     }
62     SQLALCHEMY_DATABASE_URI = get_db_uri(dbinfo)
63 # 生产环境
64 class ProductConfig(Config):
65     # 数据库信息配置
66     dbinfo = {
67         "DATABASE": "mysql",
68         "DRIVER":"pymysql",
69         "USER": "root",
70         "PASSWORD": "guoyapeng",
71         "HOST": "localhost",
72         "PORT": "3306",
73         "NAME": "FlaskModelAdvanced",
74     }
75     SQLALCHEMY_DATABASE_URI = get_db_uri(dbinfo)
76 
77 # 获取在什么环境下,不同环境下数据库不同
78 envs = {
79     "develop":  DevelopConfig,
80     "texting":  TestingConfig,
81     "staging":  StagingConfig,
82     "product":  ProductConfig,
83     "default":  ProductConfig,
84 }
85 
86 settings.py
settings.py
  • views.py
1 from movies.views import movie_blue
2 from users.views import user_blue
3 
4 # 创建的模块中的蓝图进行注册初始化
5 def init_blue(app):
6     app.register_blueprint(blueprint=user_blue)
7     app.register_blueprint(blueprint=movie_blue)

movies.py 包文件中

  • __init__.py  
  • models.py
1 from day05_chaiAdvanced.extension import db
2 
3 
4 class Movie(db.Model):
5     id = db.Column(db.Integer, primary_key=True, autoincrement=True)
6     m_name = db.Column(db.String(32))
7     m_detail = db.Column(db.String(32))
  • views.py
1 from flask import Blueprint
2 from .models import Movie 
3 
4 movie_blue = Blueprint("movie_blue",__name__,url_prefix="/movies/")
5 
6 @movie_blue.route('/')
7 def index():
8     return "Movie Index"

users.py 包文件中 和movies.py 包文件类似。为了实现相应的功能的视图和模型的编写


生如逆旅 一苇以航
原文地址:https://www.cnblogs.com/TMMM/p/11479517.html