flask实战-个人博客-使用工厂函数创建程序实例 --
使用工厂函数创建程序实例
使用蓝本还有一个重要的好处,那就是允许使用工厂函数来创建程序实例。在OOP(Object-Oriented Programming,面向对象编程)中,工厂(factory)是指创建其他对象的对象,通常是一个返回其他类的对象的函数或方法,比如我们之前的例子中创建的WTForms验证器(函数)。在personalBlog程序中,程序实例可以设计为在工厂函数中创建,这个函数返回程序实例app。按照惯例,这个函数被命名为create_app()或make_app()。我们把这个工厂函数称为程序工厂(Application Factory)--即“生产程序的工厂”,使用它可以在任何地方创建程序实例。
工厂函数使得测试和部署更加方便。我们不必将加载的配置写死在某处,而是直接在不同的地方按照需要的配置创建程序实例。通过支持创建多个程序实例,工厂函数提供了很大的灵活性。另外,借助工厂函数,我们还可以分离扩展的初始化操作。创建扩展对象的操作可以分离到单独的模块,这样可以有效减少循环依赖的发生。personalBlog程序的工厂函数如下所示:
personalBlog/__init__.py
from flask import Flask
from personalBlog.settings import config def create_app(config_name=None):
if config_name is None:
config_name = os.getenv('FLASK_CONFIG', 'development') app = Flask('personalBlog')
app.config.from_object(config[config_name]) app.register_blueprint(auth, url_prefix = '/auth')
return app
工厂函数接收配置名作为参数,返回创建好的程序实例。如果没有传入配置名,我们会从FLASK_CONFIG环境变量获取,如果没有获取到则使用默认值development。
在这个工厂函数中,我们会创建程序实例,然后为其加载配置,注册在前面创建的蓝本,最后返回程序实例。不过,现在的程序实例还没有执行扩展的初始化操作,后续一步步扩充它。
工厂函数一般在程序包的构造文件中创建,如果你愿意,也可以在程序包内新建的模块来存放,比如factory.py或是app.py。
1、加载配置
工厂函数接收配置名称作为参数,这允许我们在程序的不同位置传入不同的配置来创建程序实例。比如,使用工厂函数后,我们可以在测试脚本中使用测试配置来调用工厂函数,创建一个单独用于测试的程序实例,而不用从某个模块导入程序实例。
2、初始化扩展
为了完成扩展的初始化操作,我们需要在实例化扩展类时传入程序实例。但使用工厂函数时,并没有一个创建好的程序实例可以导入。如果我们把实例化操作放到工厂函数中,那么我们就没有一个全局的扩展对象可以使用,比如表示数据库的db对象。
为了解决这个问题,大部分扩展都提供了一个init_app()方法来支持分离扩展的实例化和初始化操作。现在我们仍然像往常一样初始化扩展类,但是并不传入程序实例。这时扩展类实例化的工作可以集中放到extension.py脚本中,如下所示:
personalBlog/extensions.py:扩展类实例化
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from flask_mail_import Mail
from flask_ckeditor import CKEditor
from flask_moment import Moment bootstrap = Bootstrap()
db = SQLAlchemy()
moment = Moment()
ckeditor = CKEditor()
mail = Mail()
现在,当我们需要在程序实例中使用扩展对象时,直接从这个extensions模块导入即可。在工厂函数中,我们导入所有扩展对象,并对其调用init_app()方法,传入程序实例完成初始化操作:
from personalBlog.extensions import bootstrap, db, moment, ckeditor, mail def create_app(config_name=None):
if config_name is None:
config_name = os.getenv('FLASK_CONFIG', 'development') app = Flask('personalBlog')
app.config.from_object(config[config_name]) app.register_blueprint(auth, url_prefix = '/auth') bootstrap.init_app(app)
db.init_app(app)
moment.init_app(app)
ckeditor.init_app(app)
mail.init_app(app) return app
3、组织工厂函数
除了扩展初始化操作,还有很多处理函数要注册到程序上,比如错误处理函数、上下文处理函数等。虽然蓝本也可以注册全局的处理函数,但是为了便于管理,除了蓝本特定的处理函数,这些处理函数一般都放到工厂函数中注册。
为了避免把工厂函数弄得太长太复杂,我们可以根据类别把这些代码分离成多个函数,这些函数接收程序实例app作为参数,分别用来为程序实例初始化扩展、注册蓝本、注册错误处理函数、注册上下文处理函数等一系列操作,如下所示:
personalBlog/__init__.py: 组织工厂函数
from personalBlog.extensions import bootstrap, db, moment, ckeditor, mail, loginManager def create_app(config_name = None):
if config_name is None:
config_name = os.getenv('FLASK_CONFIG', 'development') app = Flask('personalBlog')
app.config.from_object(config[config_name]) register_logging(app) # 注册日志处理器
register_extensions(app) # 注册扩展(扩展初始化)
register_blueprints(app) # 注册蓝本
register_commands(app) # 注册自定义shell命令
register_errors(app) # 注册错误处理函数
register_shell_context(app) # 注册错误处理函数
register_template_context(app) # 注册模板上下文处理函数
return app def register_logging(app):
pass #后续介绍日志 def register_extensions(app):
bootstrap.init_app(app)
db.init_app(app)
ckeditor.init_app(app)
mail.init_app(app)
moment.init_app(app) def register_blueprints(app):
app.register_blueprint(auth, url_prefix = '/auth') def register_shell_context(app):
@app.shell_context_processor
def make_shell_context():
return dict(db = db) def register_template_context(app):
pass def register_errors(app):
@app.errorhandler(400)
def bad_request(e):
return render_template('errors/400.html'), 400 def register_commands(app):
pass
这里的register_*函数的命名只是约定,你也可以使用configure_*或类似的命名形式。另外,你也可以按需要添加或删除对应的函数。
现在,当工厂函数被调用后。首先创建一个特定配置类的程序实例,然后执行一些列注册函数为程序实例注册扩展、蓝本、错误处理器、上下文处理器、请求处理器。。。在这个程序工厂的加工流水线的尽头,我们可以得到一个包含所有基本组件的可以直接运行的程序实例。
当使用工厂函数时,因为扩展初始化操作分离,db.create_all()将依赖于程序上下文才能正常执行。执行flask shell命令启动的python shell会自动激活程序上下文,flask命令也会默认在程序上下文环境下执行,所以目前程序中的db.create_all()方法可以被正确执行。当在其他脚本中直接调用db.create_all(),或是在普通的python shell中调用时,则需要手动激活程序上下文。
4、启动程序
当使用flask run命令启动程序时,Flask的自动发现程序实例机制还包含另一种行为:flask会自动从环境变量FLASK_APP的值定义的模块中寻找名称为create_app()或make_app()的工厂函数,自动调用工厂函数创建程序实例并运行。因为我们已经在.flaskenv文件中将FLASK_APP设为personalBlog,所以不需要更改任何设置,继续使用flask run命令即可运行程序:
flask run
如果想设置特定的配置名称,最简单的方式是通过环境变量FLASK_CONFIG设置。另外,你也可以使用FLASK_APP显示地指定工厂函数并传入参数:
FLASK_APP = "personalBlog:create_app('development')"
为了支持Flask自动从FLASK_APP环境变量对应值指向的模块或包中发现工厂函数,工厂函数中接收的参数必须是默认参数,即设置了默认值的参数,比如“config_name=None”。
5、current_app
使用工厂函数后,我们会遇到一个问题:对于蓝本实例没有提供,程序实例独有的属性和方法应该如何调用呢(比如获取配置的app.config属性)?考虑下面的因素:
1-使用工厂函数创建程序实例后,在其他模块中并没有一个创建好的程序实例可以让我们导入使用。
2-使用工厂函数后,程序实例可以在任何地方被创建。你不能固定导入某一个程序实例,因为不同程序实例可能加载不同的配置变量。
解决方法是使用current_app对象,它是一个表示当前程序实例的代理对象。当某个程序实例被创建并运行时,它会自动指向当前运行的程序实例,并把所有操作都转发到当前的程序实例。比如,当我们需要获取配置值时,会使用current_app.config,其他方法和属性也一样。
current_app是程序上下文全局变量,所以只有在激活了程序上下文之后才可以使用。比如在视图函数中,或是在视图函数中调用的函数和对象中。
flask实战-个人博客-使用工厂函数创建程序实例 --的更多相关文章
- 【flask】使用类组织配置-使用工厂函数创建程序实例
[需求] 使用配置类管理flask管理测试环境, 通过1个参数即可控制Flask是运行develpment环境还是production环境(数据库配置,邮件配置也要根据环境的变化而变化) [思路] 1 ...
- flask实战-个人博客-虚拟环境、项目结构
个人博客 博客是典型的CMS(Content Management system,内容管理系统),通常由两部分组成:一部分是博客前台,用来展示开放给所有用户的博客内容:另一部分是博客后台,这部分内容仅 ...
- flask实战-个人博客-电子邮件支持
电子邮件支持 因为博客要支持评论,所以我们需要在文章有了新评论后发邮件通知管理员.而且,当管理员回复了读者的评论后,也需要发送邮件提醒读者. 为了方便读者使用示例程序,personalBlog中仍然使 ...
- flask实战-个人博客-使用蓝本模块化程序
使用蓝本模块化程序 实例化flask提供的blueprint类就创建一个蓝本实例.像程序实例一样,我们可以为蓝本实例注册路由.错误处理函数.上下文处理函数,请求处理函数,甚至是单独的静态文件文件夹和模 ...
- flask实战-个人博客-使用类组织配置
使用类组织配置 在实际需求中,我们往往需要不同的配置组合.例如,开发用的配置,测试用的配置,生产环境用的配置.为了能方便地在这些配置中切换,你可以把配置文件升级为包,然后为这些使用场景分别创建不同的配 ...
- flask实战-个人博客-模板 --
模板 personalBlog采用典型的博客布局,左侧三分之二为主体,显示文章列表.正文:右侧三分之一为边栏,显示分为类列表.社交链接等.现在的工作是将HTML文件加工为模板,并创建对应的表单类,在模 ...
- flask实战-个人博客-编写博客前台
编写博客前台 博客前台需要开放给所有用户,这里包括显示文章列表.博客信息.文章内容和评论等功能功能. 分页显示文章列表 为了在主页显示文章列表,我们要先在渲染主页模板的index视图的数据库中获取所有 ...
- flask实战-个人博客-数据库-生成虚拟数据 --
3.生成虚拟数据 为了方便编写程序前台和后台功能,我们在创建数据库模型后就编写生成虚拟数据的函数. 1)管理员 用于生成虚拟管理员信息的fake_admin()函数如下所示: personalBlog ...
- flask实战-个人博客-表单
表单 下面我们来编写所有表单类,personalBlog中主要包含下面这些表单: 登录表单: 文章表单: 评论表单: 博客设置表单: 这里仅介绍登录表单.文章表单.分类表单和评论表单,其他的表单在实现 ...
随机推荐
- vue、element-ui开发技巧
1.vue下input文本框获得光标 html: <el-input size="mini" clearable v-model.trim="addOrEditDa ...
- scrapy中间件中发送邮件
背景介绍:之前写过通过通过scrapy的扩展发送邮件,在爬虫关闭的时候发送邮件.那个时候有个问题就是MailSender对象需要return出去.这次需要在中间件中发送邮件,但是中间件中不能随便使用r ...
- Java8实战及自己的总结
java8 介绍 java8是2014年3月份,由Oracle发布的一个版本,又称之为jdk1.8,是现再我们在学习和工作中用的最多的一个版本. 在jdk1.8中,java8以添加非常多的新特性, ...
- TestNG系列(一)TestNG+Eclipse环境配置
前提 1.JDK的安装与环境变量的配置 2.Eclipse的下载与安装 以上这些是基础中的基础,不再详细介绍. Eclipse安装TestNG插件 打开eclipse--->help---> ...
- 【数据库】Mysql配置参数
vim /ect/my.cnf 使用命令打开mysql的配置文件. 加入以下参数 [mysql] default-character-set=utf8 [mysqld] lower_case_tabl ...
- java自定义异常和throw、throws的使用
一.自定义异常类 我们知道所有的异常都是Exception,所以我们需要自定义异常只需要继承那个Exception类就可以了.下面我们自定义一个异常类,代码如下: /** * 自定义异常, */ // ...
- SQL Server 事务日志截断、回绕与收缩(转载)
每个 SQL Server 数据库都具有事务日志,用于记录所有事务以及每个事务对数据库所做的修改. 必须定期截断事务日志以避免它被填满. 但是,一些因素可能延迟日志截断,因此监视日志大小很重要. 某些 ...
- (转载) js 单引号替换成双引号,双引号替换成单引号 操作
引言:刚开始用js遇到不少问题,表示看不懂,为什么替换单引号需要/g,现在知道/g是正则中的匹配全部 原文:http://blog.csdn.net/joyhen/article/details/43 ...
- Nginx配置单项SSL以及双向SSL
Https安全协议的由来? 在实现 HTTPS协议前,我们需要了解 SSL 协议,但其实我们现在使用的更多的是 TLS 加密通讯协议. 那么TLS是怎么保证明文消息被加密的呢?在OSI七层模型中,应用 ...
- Android四大组件——Activity跳转动画、淡出淡入、滑出滑入、自定义退出进入
Activity跳转动画.淡入淡出.滑入滑出.自定义退出进入 前言: 系统中有两个定义好的Activity跳转动画:fade_in.fade_out.slide_in_left.slide_out_r ...