未命名作品.jpg


1 集成Python shell

在我们实际的开发中,不免有一些任务需要在shell下完成。比如为cms后台添加超级管理员的需求,又比如迁移数据库的需求,定时任务等等,诸如这类需求更适合在shell中去操作(大部分需要在shell中去操作的都是权限比较高的任务)。

提示:迁移数据库就是用来解决数据库更新问题,解决之前我们学的db.create_all()db.drop_all()更新数据库的时候丢失数据的问题。
flask官方提供了一个扩展组件flask-script可以实现在shell下操作我们的Flask项目。

1.1 flask-script的用法:

1 由于flask-script是Flask的一个扩展组件,同往常一样首先在虚拟环境中安装我们的flask_script包。

  1. pip install flask-script

1.1.1 实例:flask-script的简单实现

提示:实例下面有讲解
项目目录

  1. manage.py
  2. server.py
  3. ├─static # 文件夹
  4. ├─templates # 文件夹

server.py

  1. from flask import Flask
  2. app = Flask(__name__)
  3. @app.route('/')
  4. def hello_world():
  5. return 'Hello World!'
  6. if __name__ == '__main__':
  7. app.run()

manage.py

  1. from flask_script import Manager
  2. from server import app
  3. manager = Manager(app)
  4. @manager.command
  5. def hello():
  6. print('hello world')
  7. if __name__ == '__main__':
  8. manager.run()

解读:manage.py
(1)flask_script模块中导入flask_script的核心类Manager

  1. from flask_script import Manager

(2)server.py模块中把app对象导入

  1. from server import app

(3)Manager()类传入app对象实例化出manager对象,manager对象用于以后所有添加命令相关操作

  1. manager = Manager(app)

(4)利用@manager.command装饰器添加以被装饰函数的名字命名的一条命令被装饰函数的映射

  1. @manager.command # 相当于添加了一条hello命令,可以调用到hello函数
  2. def hello():
  3. print('hello world')

(5)manager调用run方法之前定义的命令才会生效

  1. if __name__ == '__main__':
  2. manager.run()

shell下操作命令
shell中切入到该manage.py的目录下,并且进入虚拟环境。输入命令python manage.py hello

  1. >>python manage.py hello

命令中的hello@manager.command装饰器装饰的函数名
执行命令后会调用hello函数
如图所示实现了调用hello函数
06-01 Flask_脚本 - 图2

1.1.1命令添加方式:

第一种(无参命令):

使用manager.commad方式添加命令:

  1. ...
  2. @manager.command
  3. def demo():
  4. print('无参命令')
  5. ...

切入到manage.py所在的目录中,切入到虚拟环境,执行如下命令

  1. >>python manage.py demo

第二种(有参命令):

使用manager.option('-简写的命令',‘--全写的命令’,dest=‘传给函数的形参’)添加命令:

  1. ...
  2. @manager.option("-u","--username",dest="username")
  3. @manager.option("-p","--password",dest="password")
  4. def login(username, password):
  5. print("用户名:{} 密码: {}".format(username,password))
  6. ...

切入到manage.py所在的目录中,切入到虚拟环境,执行如下命令.

  1. >>python manage.py login -u mark -p 123

第三种(子命令):

比如一个功能对应着很多个命令,这个时候就可以用子命令来实现,可以将这些命令的映射单独放到一个文件方便管理。在这个放着很多命令映射的文件中实例化Manager类出一个新的对象,并在manage.py文件中通过manager.add_command("子命令",Manager对象)来添加子命令
实例:
在之前的1.1.1实例的项目目录中新建文件db_script.py

  1. manage.py
  2. server.py
  3. db_script.py
  4. ├─static # 文件夹
  5. ├─templates # 文件夹

db_script.py

  1. from flask_script import Manager
  2. db_Manager = Manager()
  3. @db_Manager.command
  4. def init():
  5. print('初始迁移仓库')
  6. @db_Manager.command
  7. def migrate():
  8. print('生成迁移脚本')
  9. @db_Manager.command
  10. def upgrade():
  11. print("迁移脚本映射到数据库")

manage.py

  1. from flask_script import Manager
  2. from server import app
  3. from db_script import db_Manager # 导入子命令文件的Manager类实例化出的对象
  4. manager = Manager(app)
  5. manager.add_command("db",db_Manager) # 添加子命令
  6. ...

切入到manage.py所在的目录中,切入到虚拟环境,执行如下命令.

  1. python manage.py db init
  2. python manage.py db migrate
  3. python manage.py db upgrade

06-01 Flask_脚本 - 图3

2 项目重构

2.1 解耦配置信息以及模型文件信息触发循环导入问题

随着项目代码的增多 我们再把连接数据库的信息放到主app文件当中会应影响我们代码的可读性,那么我们相关数据库配置的信息应该放到一个config文件当中去,像我们当时加载debug配置一样使用app.config.from_object(config)一样加载我们的数据库连接信息。
新建config.py文件,把连接数据库相关的信息放到config.py中去
然后在主app文件中加载配置信息app.config.from_object(config)
config.py

  1. HOST = '127.0.0.1'
  2. PORT = '3306'
  3. DATABASE_NAME = '01_db'
  4. USERNAME = 'root'
  5. PASSWORD = 'root'
  6. DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{databasename}?charset=utf8mb4"\
  7. .format(username=USERNAME,password=PASSWORD,host=HOST,port=PORT,databasename=DATABASE_NAME)
  8. SQLALCHEMY_DATABASE_URI = DB_URI
  9. SQLALCHEMY_TRACK_MODIFICATIONS = False

那么主app中的模型的文件也十分影响代码易读性,也应该新开一个modles文件夹,把我们的模型表放到modles中去
models.py

  1. from app import db
  2. class UserInfo(db.Model):
  3. id = db.Column(db.Integer,primary_key=True,autoincrement=True,nullable=False)
  4. name = db.Column(db.String(30),server_default='',comment="姓名")
  5. tel = db.Column(db.String(16),server_default='',comment="电话"

app.py

  1. from flask import Flask
  2. from flask_sqlalchemy import SQLAlchemy
  3. import config
  4. from models import UserInfo
  5. app = Flask(__name__)
  6. app.config.from_object(config)
  7. db = SQLAlchemy(app)
  8. @app.route('/')
  9. def hello_world():
  10. return 'Hello World!'
  11. if __name__ == '__main__':
  12. app.run()

这是代码易读性提高了,但是新的问题随之出现了,出现了一个循环导入的问题。
app.py 文件导入了models,我们python中而导入文件必然会把需要导入的文件从上到下执行一遍,那么就触发了models的执行,而models执行的时候需要从app导入db,出现了一个死循环如下图,这就是python循环导入的问题。
06-01 Flask_脚本 - 图4

2.2 重构项目解决循环导入问题

为了解耦配置信息以及模型表信息,导致了models.pyapp.py出现了循环导入问题,我们的解决方案是新开启一个文件exts.py,在exts.py中生成db对象,解决循环导入问题。
06-01 Flask_脚本 - 图5
实例2.2.1:解决循环导入问题之后重构项目
项目目录:

  1. app.py
  2. config.py
  3. exts.py
  4. models.py
  5. ├─static # 文件夹
  6. ├─templates # 文件夹

config.py

  1. HOST = '127.0.0.1'
  2. PORT = '3306'
  3. DATABASE_NAME = '01_db'
  4. USERNAME = 'root'
  5. PASSWORD = 'root'
  6. DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{databasename}?charset=utf8mb4"\
  7. .format(username=USERNAME,password=PASSWORD,host=HOST,port=PORT,databasename=DATABASE_NAME)
  8. SQLALCHEMY_DATABASE_URI = DB_URI
  9. SQLALCHEMY_TRACK_MODIFICATIONS = False

exts.py

  1. from flask_sqlalchemy import SQLAlchemy
  2. db = SQLAlchemy()

models.py

  1. from exts import db
  2. class UserInfo(db.Model):
  3. __tablename__ = 'user_info'
  4. id = db.Column(db.Integer, primary_key=True, autoincrement=True)
  5. username = db.Column(db.String(20),nullable=False,server_default='')

app.py

  1. from flask import Flask
  2. from exts import db
  3. import config
  4. from models import UserInfo
  5. app = Flask(__name__)
  6. app.config.from_object(config)
  7. db.init_app(app)
  8. @app.route('/')
  9. def hello_world():
  10. return 'Hello World!'
  11. if __name__ == '__main__':
  12. app.run()

3 使用Flask-Migrate迁移数据库

之前我们更新数据库的方式是先删除表然后再创建表简单粗暴,但是会丢失掉所有原来表中的数据。做web开发的我们应该深知数据无价,所以这个时候需要数据库迁移工具来完成这个工作,SQLAlcheme的开发者Michael Bayer开发了一个数据库迁移工具—-Alembic来实现数据库的迁移,SQLAlchemy翻译成汉语是炼金术,而蒸馏器(Alembic)正是炼金术士最需要的工具。
我们的flask-sqlalchmy扩展组件正是基于SQLAlchemy,当然Flask也有专门做数据库迁移的扩展组件Flask-Migrate,同样Flask-Migrate正是基于Alembic
06-01 Flask_脚本 - 图6

3.1 Flask-Migrate的用法:

1 由于flask-migrate是Flask的一个扩展组件,同往常一样首先在虚拟环境中安装我们的flask_migrate包。

  1. pip install flask-migrate

为了导出数据库迁移命令,Flask-Migrate提供了一个MigrateCommand类,可附加到Flask-Script的manager对象上。在这个例子中,MigrateCommand类使用db命令附加。
我们的Flask_Migrate的操作是在shell下完成的,所以要基于Flask-scriptFlask-Migrate提供了一个MigrateCommand类,需要附加到Flask-Scriptmanager对象上,完成命令的创建,并且Flask_Migrate同时体统了Migrate类,需要加载核心对象app和数据库对象db。完成迁移工具的配置。

实例3.1.1:配置Flask_Migrate

首先在实例2.2.1中创建manage.py
manage.py代码如下

  1. from flask_script import Manager
  2. from flask_migrate import Migrate,MigrateCommand
  3. from exts import db
  4. from server import app
  5. manager = Manager(app)
  6. Migrate(app,db)
  7. manager.add_command('db',MigrateCommand)

解读:
(1) 首先从flask_migrate中导入 Migrate,MigrateCommand

  1. from flask_migrate import Migrate,MigrateCommand

(2)Migrate加载app对象和db对象获取数据库的配置信息以及模型表信息。

  1. Migrate(app,db)

(3)MigrateCommand附加到manager创建迁移数据库的子命令

  1. manager.add_command('db',MigrateCommand)

迁移脚本命令

(1) 创建迁移仓库
首先切换到项目目录下并且切入到虚拟环境中输入命令python manage.py db init

  1. >> python manage.py db init

该命令初始化我们的迁移仓库,并且在我们的项目目录中创建迁移仓库文件
06-01 Flask_脚本 - 图7
(2) 创建迁移脚本
依然在我们的shell中输入命令python manage.py db migrate

  1. >> python manage.py db migrate

该命令会在数据库创建一张 alembic_version 表,存放着数据库迁移脚本的版本信息,该命令会搜集到需要迁移的模型表信息,写入到脚本中,但是并没有真正的映射到数据库中。
(3)更新数据库
依然在我们的shell中输入命令python manage.py db upgrade

  1. python manage.py db upgrade

对于第一次迁移来说,其作用和db.createall()方法一样,但是在随后的迁移中,upgrade命令可以把模型表改动的部分映射到数据库中,实现了一个更新的效果,并且不影响之前保存的数据。
提示:在首次执行这个命令之前如果该数据库的库内已经有了一些表,并且这些表没有与我们的模型映射,会自动删除掉这些表。_