2019-06-09第六集

Flask之大型程序结构

使用包和模块组织大型程序

项目结构:

|-flasky
  |-app/
    |-templates/
    |-static/
    |-main/
      |-__init__.py
      |-errors.py
      |-forms.py
      |-views.py
    |-__init__.py
    |-email.py
    |-models.py
  |-migrations/
  |-tests/
    |-__init__.py
    |-test*.py
  |-venv/
  |-requirements.txt
  |-config.py
  |-manage.py

四个顶级文件夹

  • Flask程序一般都保存在名为app的包中
  • 和之前一样,migrations文件夹包含数据库迁移的脚本
  • 单元测试编写在tests包中
  • 和之前一样,侧女文件夹包含Python虚拟环境

并同时创建一些新文件:

  • requirements.txt列出了所有依赖包,便于在其他电脑中重新生成相同的虚拟环境
  • config.py存储配置
  • manage.py用于启动程序以及其他的程序任务

把hello.py转换成这种结构的过程

配置选项

程序经常需要设定多个配置,开发、测试和生产环境要使用不同的数据库,这样才不会彼此影响。
使用层次结构的配置类。

#程序的配置:基类 Config 中包含通用配置,子类分别定义专用的配置
import os 
basedir=os.path.abspath(os.path.dirname(__file__))

class Config:
  SECRET_KEY=os.environ.get('SECRET_KET') or 'hard ro guess string'
  #Flask(以及相关的扩展extension)需要进行加密
  SQLALCHEMY_COMMIT_ON_TEARDOWN=True
  #将其设为True时,每次请求结束后都会自动提交数据库中的变动。
  FLASKY_MAIL_SUBJECT_PREFIX=['Flasky']
  FLASKY_MAIL_SENDER='Flasky Admin '
  FLASKy_ADMIN=os.environ.get('FLASKY_ADMIN')

  @staticmethod
  def init_app(app):
    pass

class DevelopmentConfig(Config):
  DEBUG=True
  MAIL_SERVER='smtp.googlemail.com'
  MAIL_PORT=587
  MAIL_USE_TLS=True
  MAIL_USERNAME=os.environ.get('MAIL_USERNAME')
  MAIL_PASSWORD=os.environ.get('MAIL_PASSWORD')
  SQLALCHEMY_DATABASE_URL=os.environ.get('DEV_DATABASE_URL') or \'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')

class TestingConfig(Config):
  TESTING=True
  SQLALCHEMY_DATABASE_URL=os.environ.get('TEST_DATABASE_URL') or \'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')

class ProductionConfig(Congfig):
  SQLCHEMY_DATABASE_URL=os.environ.get('DATABASE_URL') or \'sqlite:///' + os.path.join(basedir, 'data.sqlite')

#在 3 个子类中, SQLALCHEMY_DATABASE_URI 变量都被指定了不同的值。这样程序就可在不同的配置环境中运行,每个环境都使用不同的数据库。

config={
      'development':DevelopmentConfig,
      'testing':TestingConfig,
      'production':ProduvtionConfig,
      'default':DevelopmentConfig
}

程序包

程序包用来保存程序的所有代码,模板和静态文件

使用程序工厂函数

为了可以修改配置,可以延迟创建程序实例,把创建过程移到可显式调用的工厂函数中,这样可以创建多个程序实例。


程序的工厂函数在app包的构造文件中定义

#app/__init__.py:程序包的构造文件
from flask import Flask,render_template
from flask.ext.boorstrap import Bootstrap
from flask.ext.mail import Mail
from flask.ext.moment import Moment
from flask.ext.sqlalchemy import SQLALchemy
from onfig import config

bootstrap=Bootstrap()
mail=Mail()
moment=Moment()
db=SQLALchemy()

#creat_app()函数就是程序的工厂函数
def creat_app(config_name):
  app=Flask(__name__)
  app.config.from_object(config[config_name])
  #配置类在 config.py 文件中定义,其中保存的配置可以使用 Flask app.config 配置对象提供的 from_object() 方法直接导入程序。
  config[config_name].init_app(app)

  bootstrap.init_app(app)
  mail.init_app(app)
  moment.init_app(app)
  db.init_app(app)

  #附加路由和自定义的错误界面
  return app

在蓝本中实现程序功能

在蓝本中定义的路由处于休眠状态,直到蓝本注册到程序上后,路由才真正成为程序的一部分。为了获得最大的灵活性,程序包中创建了一个子包,用于保存蓝本。

#app/main/__init__.py:创建蓝本
from flask import Blueprint

main-Blueprint('main',__name__)     #通过实例化一个 Blueprint 类对象可以创建蓝本。这个构造函数有两个必须指定的参数:蓝本的名字和蓝本所在的包或模块。

from . import views,errors

程序的路由保存在包里的 app/main/views.py 模块中,而错误处理程序保存在 app/main/errors.py 模块中。导入这两个模块就能把路由和错误处理程序与蓝本关联起来。

#app/__init__.py:注册蓝本
def creat_app(config_name):
  #...
  from .main import main as main_blueprint
  app.register_blueprint(main_blueprint)

  return app

#app/main/errors.oy 蓝本中的错误处理程序
在蓝本中编写错误处理程序稍有不同,如果使用 errorhandler 修饰器,那么只有蓝本中的错误才能触发处理程序。要想注册程序全局的错误处理程序,必须使用 app_errorhandler 。
from flask import render_template
from . import main

@main.app_errorhandler(404)
def page_not_found(e):
  return render_template('404.html'),404

@main.app_errorhandler(500)
def internal_server_error(e):
  return render_template('500.html'),500

#app/main/views.py蓝本中定义的程序路由
from datetime import datetime
from flask import render_tempalte,session,redirect,url_for

from . import main
from .froms import NameForm
from .. import db 
from ..models import User

@main.route('/',methods=['GET','POST'])
def index():
  form=NameForm()
  if form.validate_on_submit():
    #...
    return redirect(url_for('.index'))
  return render_tempalte('index.html',form=form,name=session.get('name'),known=session.get('known',Flase),current_time=datetime.utcnow())
#url_for() 函数还支持一种简写的端点形式,在蓝本中可以省略蓝本名,例如 url_for('.index') 。在这种写法中,命名空间是当前请求所在的蓝本。

启动脚本

顶级文件夹中的manage.py文件用于启动程序

#manage.py:启动脚本
import os 
from app import creat_app,db
from app.models import User,Role
from flask.ext.script import Manager.Shell
from flask.ext.migrate import Migrate,MigrateCommand

app=creat_app(os.getenv('FLASK_CONFIG') or 'default')
manager=Manger(app)
migrate=Migrate(app,db)

def make_shell_context():
  return dict(app=app,db=db,User=User,Role=Role)
manager.add_command('shell',shell(make_context=make_shell_context))
manager.add_command('db',MigrateCommand)

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

需求文件

程序中必须包含一个 requirements.txt 文件,用于记录所有依赖包及其精确的版本号。

Flask==0.10.1
Flask-Bootstrap==3.0.3.1
Flask-Mail==0.9.0
Flask-Migrate==1.1.0
Flask-Moment==0.2.0
Flask-SQLAlchemy==1.0
Flask-Script==0.6.6
Flask-WTF==0.9.4
Jinja2==2.7.1
Mako==0.9.1
MarkupSafe==0.18
SQLAlchemy==0.8.4
WTForms==1.0.5
Werkzeug==0.9.4
alembic==0.6.2
blinker==1.3
itsdangerous==0.23

如果你要创建这个虚拟环境的完全副本,可以创建一个新的虚拟环境,并在其上运行以下命令:

(venv) $ pip install -r requirements.txt

单元测试

tests/test_basics.py:单元测试
from unittest
from flask import current_app
from app import create_app,db

class BasicsTestCase(unittest.TestCase):
  def setUp(self):
    self.app=create_app('testing')
    self.app_context=self.app.app_context()
    self.app_context.push()
    db.create_all()

  def teatDown(self):
    db.session.remove()
    db.drop_all()
    self.app_context.pop()

  def test_app_exists(self):
    self.assertFalse(current_app is None)

  def test_app_is_testing(self):
    self.assertTrue(current_app.config['TESTING'])
#如果你想进一步了解如何使用 Python 的 unittest 包编写测试,请阅读官方文档(https://docs.python.org/2/library/unittest.html)。

#manger.py:启动单元测试的命令
@manager.command
def test():
"""Run the unit tests."""
  import unittest
  tests = unittest.TestLoader().discover('tests')
  unittest.TextTestRunner(verbosity=2).run(tests)

创建数据库

首选从环境变量中读取数据库的 URL,同时还提供了一个默认的 SQLite 数据库做备用。3
种配置环境中的环境变量名和 SQLite 数据库文件名都不一样。例如,在开发环境中,数据
库 URL 从环境变量 DEV_DATABASE_URL 中读取,如果没有定义这个环境变量,则使用名为
data-dev.sqlite 的 SQLite 数据库。


不管从哪里获取数据库 URL,都要在新数据库中创建数据表。如果使用 Flask-Migrate 跟
踪迁移,可使用如下命令创建数据表或者升级到最新修订版本:

(venv) $ python manage.py db upgrade

你可能感兴趣的:(2019-06-09第六集)