zoukankan      html  css  js  c++  java
  • 9、大型程序的结构

    1、目录结构

    目录:

    Flask程序一般保存在名为app的包中

    migrations文件夹包含数据库迁移脚本

    单元测试编写在test包中

    venv文件夹包含python虚拟环境

    文件:

    requirements.txt 列出来了所有依赖包,便于在其他电脑中重新生成相同的虚拟环境

    config.py 存储配置

    manage.py 用于启动程序以及其他的程序任务

    2、配置选项

    config.py

    原先采用的是在hello.py中简单的字典状结构配置,现在使用层次结构的配置类

    import os
    basedir = os.path.abspath(os.path.dirname(__file__))
    
    
    class Config:
        SECRET_KEY = '123456'
        SQLALCHEMY_COMMIT_ON_TEARDOWN = True
        FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
        FLASKY_MAIL_SENDER = '*****@qq.com'
        FLASKY_ADMIN = '*****@qq.com'
    
        @staticmethod
        def init_app(app):
            pass
    
    
    class DevelopmentConfig(Config):
        DEBUG = True
        MAIL_SERVER = 'smtp.qq.com'
        MAIL_PORT = 465
        MAIL_USE_TLS = False
        MAIL_USE_SSL = True
        MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
        MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
        SQLALCHEMY_DATABASE_URI = 'sqlite:///data-dev.sqlite'
    
    
    class TestingConfig(Config):
        TESTING = True
        SQLALCHEMY_DATABASE_URI = 'sqlite:///data-test.sqlite'
    
    
    class ProductionConfig(Config):
        SQLALCHEMY_DATABASE_URI = 'sqlite:///data.sqlite'
    
    
    config = {
        'development': DevelopmentConfig,
        'testing': TestingConfig,
        'production': ProductionConfig,
    
        'default': DevelopmentConfig
    }

    基类config.py中包含通用配置,子类分别电仪了专用的配置,某些敏感的配置可以从环境变量中导入

    SQLALCHEMY_DATABASE_URI变量都被指定了不同的值,程序可以在不同配置环境下运行,每个环境使用不同的数据库

    配置类可以定义init_app()类方法,其参数是程序实例,可以执行对当前环境的配置初始化。本例中,基类Config中的init_app()方法为空

    在这个配置脚本末尾,config字典中注册了不同的配置环境,而且还注册了一个默认的配置(本例的开发环境)

    3、程序包

    程序包用来保存程序的所有代码、模板和静态文件。可以直接称为app,如有需求,也可使用专用名称。templates和static文件夹是程序包的一部分,数据库模型和电子邮件支持含函数也被移到这个包中

    本例中将原先templates下的文件转移到app/templates下

    4、使用程序工厂函数

    app/__init__.py 程序包的构造文件

    create_app()函数就是程序的工厂函数,接受一个函数,是程序使用的配置名。配置名在config.py文件中定义,其中保存的配置可以使用Flask.app.config配置对象提供的from_object()方法直接导入程序。至于配置对象,则可以通过名字从config字典中选择,程序配置完成后,就能初始化扩展了,在之前创建的扩展对象上调用init_app()可以完成初始化过程

    from flask import Flask, render_template
    from flask_bootstrap import Bootstrap
    from flask_mail import Mail
    from flask_moment import Moment
    from flask_sqlalchemy import SQLAlchemy
    from config import config
    
    bootstrap = Bootstrap()
    mail = Mail()
    moment = Moment()
    db = SQLAlchemy()
    
    def create_app(config_name):
        app = Flask(__name__)
        app.config.from_object(config[config_name])
        config[config_name].init_app(app)
    
        bootstrap.init_app(app)
        mail.init_app(app)
        moment.init_app(app)
        db.init_app(app)
    
        #附加路由和自定义的错误页面
    
        return app

    5、在蓝本中实现程序功能

    直接使用app.route修饰器定义路由,在程序运行时才创建,只有调用create_app之后才能使用app.route修饰器。

    在蓝本中定义的路由处于休眠状态,直到蓝本注册到程序上后,路由才真正成为程序的一部分。使用位于全局作用域中的蓝本时,定义路由的方法几乎和单脚本程序一样,为获取最大的灵活性,程序包中创建了一个子包用于保存蓝本

    创建蓝本

    app/main/__init__.py

    实例化Blueprint类对象创建蓝本:参数1,蓝本的名字和蓝本所在的包或模块;参数2,使用__name__就好

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

    注:

    这些模块在app/main/__init__.py 脚本的末尾导入,这是为了避免循环导入依赖,因为在views.py和errors.py中还要导入蓝本main

    from flask import Blueprint
    
    main = Blueprint('main', __name__)
    
    from . import views, errors

    6、注册蓝本

    app/__init__.py

    蓝本在工厂函数create_app()中注册到程序中

    from flask import Flask, render_template
    from flask_bootstrap import Bootstrap
    from flask_mail import Mail
    from flask_moment import Moment
    from flask_sqlalchemy import SQLAlchemy
    from config import config
    
    bootstrap = Bootstrap()
    mail = Mail()
    moment = Moment()
    db = SQLAlchemy()
    
    def create_app(config_name):
        app = Flask(__name__)
        app.config.from_object(config['default'])
        config['default'].init_app(app)
    
        bootstrap.init_app(app)
        mail.init_app(app)
        moment.init_app(app)
        db.init_app(app)
    
        #注册蓝本
        from .main import main as main_blueprint
        app.register_blueprint(main_blueprint)
    
        #附加路由和自定义的错误页面
    
        return app

    蓝本中的错误处理程序

    app/main/errors.py

    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

    在蓝本中编写错误处理程序有所不同,如果使用errorhandler修饰器,那么只有蓝本中的错误才能触发处理程序,要想注册程序全局的错误处理程序,必须使用app_errorhandler

    蓝本中定义的程序路由

    app/main/view.py

    在蓝本中编写视图函数不同点

    (1)和错误处理程序一样,路由修饰器由蓝本提供

    (2)url_for()函数的用法不同,在程序的路由中,url_for()第一个参数是视图函数的名称,在蓝本中,Flask会为蓝本中的全部端点加上一个命名空间,这样可以在不同的蓝本中使用相同的端点名定义视图函数,而不会产生冲突。命名空间名称(Blueprint构造函数的第一个参数)就是蓝本的名称,所以视图函数index()注册的端点名是main.index,其URL使用url_for('main.index')获取。

    url_for('.index')是蓝本中一种简写的端点形式,但在跨蓝本的重定向必须使用带有端点命名空间的端点名

    from datetime import datetime
    from flask import render_template, session, redirect, url_for
    from . import main
    from .forms import NameForm
    from .. import db
    from ..models import User
    
    @main.route('/', methods=['GET', 'POST'])
    def index():
        form = NameForm()
        if form.validate_on_submit():
            user = User.query.filter_by(username=form.name.data).first()
            if user is None:
                user = User(username=form.name.data)
                db.session.add(user)
                session['known'] = False
                #if app.config['FLASKY_ADMIN']:
                    #send_mail(app.config['FLASKY_ADMIN'], 'New User', 'mail/new_user', user=user)
            else:
                session['known'] = True
            session['name'] = form.name.data
            form.name.data = ''
            return redirect(url_for('.index'))
        return render_template('index.html', form = form, name = session.get('name'), known = session.get('known', False), current_time=datetime.utcnow())

    7、启动脚本

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

    manage.py

    先读取配置名:如果已经定义了环境变量FLASK_CONFIG,则从众读取配置名;否则使用默认配置

    初始化Flask-Script、Flask-Migrate和为python shell定义的上下文

    #!/usr/bin/env python
    import os
    from app import create_app, db
    from app.models import User, Role
    from flask_script import Manager, Shell
    from flask_migrate import Migrate, MigrateCommand
    
    app = create_app(os.getenv('default'))
    manager = Manager(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()

    8、需求文件

    更新需求文件

    pip freeze >requirements.txt

    9、单元测试

    tests/test_basics.py

    import 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 tearDown(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'])

    10、启动单元测试

    manage.py 

    manager.command修饰器让自定义命令变得简单。修饰函数名就是命令名,函数的文档字符串会显示在帮助信息中。test()函数的定义体中调用了unittest包提供的测试运行函数

    #!/usr/bin/env python
    import os
    from app import create_app, db
    from app.models import User, Role
    from flask_script import Manager, Shell
    from flask_migrate import Migrate, MigrateCommand
    
    app = create_app(os.getenv('default'))
    manager = Manager(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)
    
    #启动单元测试
    @manager.command
    def test():
        """Run the unit tests."""
        import unittest
        tests = unittest.TestLoader().discover('tests')
        unittest.TextTestRunner(verbosity=2).run(tests)
    
    if __name__ == '__main__':
        manager.run()

    11、运行单元测试

    python manage.py test

    不知道咋回事,头疼,待查

    12、创建数据库

    python manage.py db upgrade

  • 相关阅读:
    2月25日
    Maven启动tomcat7:run运行异常:org.apache.catalina.LifecycleException: Failed to start component
    Junit测试报错:java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing
    tomcat启动控制台乱码
    Java虚拟机内存详解
    Ajax使用
    Struts2工作流程
    java.lang.IllegalArgumentException: 'sessionFactory' or 'hibernateTemplate' is required
    Spring-Aop的两种代理方式
    springboot与dubbo整合遇到的坑
  • 原文地址:https://www.cnblogs.com/lw-monster/p/11784975.html
Copyright © 2011-2022 走看看