内置对象
- 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: 面向接口编程
面向切面:
在不修改原有逻辑代码的基础上。动态的去添加一些功能或者控制逻辑(装饰器类似)
重要概念:
切面:切开后可以获取的东西叫切面。(在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
- 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 包文件类似。为了实现相应的功能的视图和模型的编写