通常一个项目的启动文件为app.py或者manage.py,所以我们先将项目中启动文件的文件名进行修改。这里我修改为manage.py。
在立项准备的时候,我们在manage.py中写入了太多的内容,导致整个文件已经非常的臃肿了,所以,这里我们要对代码进行抽取,我们只让manage.py做一个程序的启动入口,而不是让所有代码都写入其中。
这里,我们自顶向下开始抽取。
第一步:配置文件抽取
1.在项目目录下新建config.py
2.将配置类从manage.py中拿到config.py中。(原来的代码不要直接剪切,复制到新文件中,然后将原来的注释掉,防止改错)
3.哪里报错改哪里
config.py
from redis import StrictRedis
manage.py
from config import Config
补充说明
封装代码的步骤:
- 确定哪些代码需要封装
- 确定要封装到哪里
- 将代码移动到指定位置
- 回看是否有错
- 如果有错就改错
- 改错后直接运行
- 注意点
- 切记不要一次封装太多代码
- 如果逻辑复杂,分批逐步封装
第二步:抽取app
我们的目的是让manage.py成为程序的入口,所以,这里在manage.py中的相关app设置,我们也要进行抽取。
app的抽取,我们只抽取标注的部分,因为我们启动用的是manage.py,而后面的部分和manage有很多的联系,抽取反而带来麻烦。
其实,app的相关代码都是业务逻辑相关代码,所以,我们最好将其封装到业务逻辑部分中。
这里,我们需要新建一个Python Package的包,命名为info,以后里面就存储着业务逻辑相关代码。
这里,我们将标注的部分抽取到info的__init__中,因为业务逻辑只要一开始,就会优先执行__init__文件中的内容。
当把代码放入到__init__中,可以看到下面内容:
一堆包需要导入,是不是满头包了啊!其实让大家抽取的时候注释原来的代码还有个好处,就是方便收集导包信息,我们将那些注释后变灰的导包代码都收集起来,同意放到__init__中,这样会方便很多。
__init__中的导包问题解决之后,我们来解决manage.py中的导包问题。
manage.py
from info import app, db
到目前位置,封装的差不多了,我们再次运行代码,看看是否可以正常运行。
第三步:抽取不同环境下的配置
不同开发环境会有不同的配置,所以我们要封装不同开发环境下的配置信息。
config.py
# 以下代码是封装不同开发环境下的配置信息 class DevlopmentConfig(Config): """开发环境""" pass # 开发环境与父类基本一致 class ProductionConfig(Config): """生产环境""" DEBUG = False SQLALCHEMY_DATABASE_URI = \'mysql://root:mysql@127.0.0.1:3306/production_news_data\' class UnittestConfig(Config): """测试环境""" pass
这里定义了三个类,它们都继承自我们定义的Config配置类,我们可以在里面对配置类中的相关信息进行重写。
重写之后,就可以去info中的__info__修改指定的配置类:
但是!这个封装有瑕疵,我们经常要根据不用的运行环境来修改配置信息,而这些东西改的太多,有时候会忘记,所以,还需要更进一步的处理,这也是下一步要完成的内容。
第四步:工厂方法创建app(烧脑的东西)
这里专门解释下为啥要用工厂方式来创建app,因为app的相关信息的修改和业务逻辑有关。而你们公司会有个恶心的职业,叫做测试,每当我们修改了业务逻辑的相关内容,都必须要进行测试,哪怕只是加了个空格,改了标点也要重新测试(没错,就是这么恶心)。所以,你懂的!
而目前和业务逻辑无关的是manage.py和config.py我们要看看从哪个文件入手,来间接地处理这个问题,这里我们选择的是manage.py。
1.创造创建app的函数
info -> __init__.py
from flask import Flask from flask_sqlalchemy import SQLAlchemy from redis import StrictRedis from flask_wtf.csrf import CSRFProtect from flask_session import Session from config import Config, DevlopmentConfig, ProductionConfig, UnittestConfig def create_app(config_name): """创建app的工厂方法 参数:根据参数选择不同的配置类 """ app = Flask(__name__) # 获取配置信息 app.config.from_object(config_name) # 创建连接到MySQL数据库的对象 db = SQLAlchemy(app) # 创建连接到Redis数据库的对象 redis_store = StrictRedis(host=config_name.REDIS_HOST, port=config_name.REDIS_PORT) # 开启CSRF保护:因为项目中的表单不再使用FlaskForm来实现,所以不会自动地开启CSRF保护,需要我们自己开启 CSRFProtect(app) # 指定session存储在后端的位置 Session(app)
当我们这么玩的时候,manage.py中的app和db导包的时候发生了错误,这是因为我们把这部分内容放到了函数中,尴尬的是,这样我们就没法从__init__中读取这两个内容了。这里先不用解决,放着就好。
2. 在manage.py中使用之前定义的创建app的函数来创建app
现在有两个问题需要解决:
一个是db,这个东西目前解决不了,先给它注释掉,别让它报错了。
还有一个是app,这个我们可以使用之前的函数来创建,而在函数中,我们指定了一个接收参数来指定配置环境,这个怎么完成呢?
这里给大家提供一种思路。
配置信息的相关设置在config.py中,所以在这个文件中定义一个字典,存储关键字对应的不同的配置类的类名
# 定义字典,存储关键字对应的不同的配置类的类名 configs = { \'dev\': DevlopmentConfig, \'pro\': ProductionConfig, \'unit\': UnittestConfig }
那么在创建app的时候,我们就可以加入这个参数了。
manage.py
# 创建app app = create_app("dev")
好了,参数传到了__init__中去创建app了。但是__init__中并不知道这个参数的作用,所以需要导入相关包,并完成相关配置:
好了,大概搞完了,运行一下看看吧!
测试之后,会发现报了下面的错误:
这个问题,一般来说你是找不到了,直接告诉你。我们在写创造app的工厂函数的时候,配置了一堆东西,作用是干嘛?不就是造一个app吗?你造完了之后返回了吗?
所以,在工厂函数的后面加上:
return app
这次应该就没有毛病了!
第五步:设置全局db
在前面,解决工厂函数的问题的时候,为了不报错,我们将db给注释了,还记得没?这是”病“不能拖,我们开始“治病”。
想要解决db的问题,第一个要点就是能看懂源代码!所以,我们从源代码来分析解决的方法。
CTRL+左键,点下面的,我们开始看源码:
初始化app,不知道是啥? CTRL+左键,点进去。
这里面恰好要从app的相关配置中读取数据库的连接信息。所以,我们没有传入app的话,就创建不了连接到MySQL数据库的对象。也就是说,我们只要调用这个init_app初始化函数,并将app传进去就完事了。
.... # 创建SQLAlchemy对象 db = SQLAlchemy() def create_app(config_name): """创建app的工厂方法 参数:根据参数选择不同的配置类 """ .... # 创建连接到MySQL数据库的对象 # db = SQLAlchemy(app) db.init_app(app) ....
再把manage.py中的注释打开,测试一下代码吧!