美文网首页你应该会玩 Flask
第六章 实例项目的描述

第六章 实例项目的描述

作者: 藕丝空间 | 来源:发表于2017-11-06 18:30 被阅读15次

    尽管在单一脚本中编写小型WEB程序很方便,但是这种方法并不能广泛使用。程序变复杂之后,使用单个大型源码文件会导致后期的维护及扩展困难。但是不同于其它的WEB框架,Flask并不强制要求大型项目使用特定的组织方式,程序结构的组织方式完全由开发者决定。

    项目的结构

    ousimd/
    
    |-- flask/
    
        |-- <python虚拟环境>
    
    |-- app/ <项目的模块名称>
    
        |-- static/ <静态文件夹>
    
        |-- templates/ <HTML模板文件夹>
    
            |-- main/ <前端蓝图>
    
                |-- __init__.py
    
                |-- views.py <路由和视图函数文件>
    
                |-- forms.py <表单类文件, wtforms插件必须项>
            |-- admin/ <后台管理蓝图>
    
                |-- __init__.py
    
                |-- views.py <路由和视图函数文件>
    
                |-- forms.py <表单类文件, wtforms插件必须项>
    
        |-- __init__.py
    
        |-- another.py <可选项,根据需要增加>
    
        |-- models.py <数据库模型文件>
    
    |-- migrations/ <数据库表关系文件夹,Flask-Migrate迁移数据库时使用>
    
    |-- tests/
    
        |-- __init__.py
    
        |-- test*.py <测试文件>
    
    |-- requeirements.txt <插件依赖包>
    
    |-- config.py <项目的配置文件>
    
    |-- manage.py <用于启动程序以及其它程序任务>
    
    

    配置选项

    在项目中配置config.py

    # -*- coding:utf-8 -*-
    __author__ = u'东方鹗'
    
    import os
    import hashlib
    
    basedir = os.path.abspath(os.path.dirname(__file__))
    
    
    class Config(object):
        SECRET_KEY = os.environ.get('SECRET_KEY') or hashlib.new(name='md5', string='ousikeji@#').hexdigest()
        SQLALCHEMY_COMMIT_ON_TEARDOWN = True    
    
        @staticmethod
        def init_app(app):
            pass
    
    
    class DevelopmentConfig(Config):
        DEBUG = True
        SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
                                'sqlite:///' + os.path.join(basedir, 'data_dev.sqlite')
    
    
    class TestingConfig(Config):
        TESTING = True
        SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \
                                'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')
    
    
    class ProductionConfig(Config):
        SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
                                'sqlite:///' + os.path.join(basedir, 'data.sqlite')
    
    
    config = {
        'development': DevelopmentConfig,
        'testing': TestingConfig,
        'production': ProductionConfig,
        'default': DevelopmentConfig
    }
    

    基类Config中包含通用配置,子类分别定义专用的配置。如果需要,你还可以添加其它配置类。

    为了让配置方式更加灵活且安全,某些配置可以从环境变量中导入。例如,SECRET_KEY的值,这是个敏感信息,可以在环境中设定,但系统也提供了一个默认值,以防环境中没有定义。
    在3个子类中,SQLALCHEMY_DATABASE_URI变量都被指定了不同的值,这样程序就可以在不同的配置环境中运行,每个环境都使用不同的数据库。其中SQLALCHEMY_DATABASE_URI变量对应的数据库引擎在 ** 《第七章 使用 Flask 扩展管理数据库》 ** 有详细介绍。
    配置类可以定义init_app()类方法,其参数是程序实例。在这个方法中,可以执行对当前环境的配置初始化。现在,基类Config中的init_app()方法为空。

    程序包

    程序包是用来保存程序的所有代码、模板和静态文件。我们可以把这个包直接成为app(应用),如果有需求,也可使用一个程序专用名字。templates和static文件夹是程序包的一部分,因此这两个文件夹被移到了app中。数据库模型app/models.py和其他专用支持函数也被移到这个包中。

    使用工厂函数

    构造文件导入了大多数正在使用的Flask扩展。由于尚未初始化所需的程序实例,所以没有初始化扩展,创建扩展类时没有向构造函数传入参数。create_app()函数就是程序的工厂函数,接受一个参数,是程序使用的配置名。配置类在config.py文件中定义,其中保存的配置可以使用Flask的app.config配置对象提供的from_object()方法直接导入程序。至于配置对象,测可以通过名字从config字典中选择。程序创建并配置好后,接能初始化扩展了。在之前创建的扩展对象上调用init_app()可以完成初始化过程。

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

    # -*- coding:utf-8 -*-
    __author__ = '东方鹗'
    
    from flask import Flask
    from config import config
    
    # db = SQLAlchemy()
    
    def create_app(config_name):
        """ 使用工厂函数初始化程序实例"""
        app = Flask(__name__)
        app.config.from_object(config[config_name])
        config[config_name].init_app(app=app)
    
        # db.init_app(app=app)
    
        return app
    

    工厂函数返回创建的程序示例,不过要注意,现在工厂函数创建的程序还不完成,在今后会陆续添加和完善。

    使用蓝图

    为了获得最大的灵活性,程序包中创建一个子包,用于保存蓝图。

    创建 蓝图main和蓝图admin

    app/main/__init__.py

    # -*- coding:utf-8 -*-
    __author__ = '东方鹗'
    
    
    from flask import Blueprint
    
    main = Blueprint('main', __name__)
    
    # 在末尾导入相关模块,是为了避免循环导入依赖,因为在下面的模块中还要导入蓝本main
    from . import views, errors
    

    app/admin/__init__.py

    # -*- coding:utf-8 -*-
    __author__ = '东方鹗'
    
    
    from flask import Blueprint
    
    admin = Blueprint('admin', __name__)
    
    # 在末尾导入相关模块,是为了避免循环导入依赖,因为在下面的模块中还要导入蓝本main
    from . import views, errors
    

    通过实例化一个Blueprint类对象可以创建蓝图。这个构造函数有两个必须指定的参数:蓝图的名字和蓝图所在的包或模块。和程序一样,大多数情况下第二个参数使用Python的__name__变量即可。

    蓝图在工厂函数create_app()中注册

    app/__init__.py中注册蓝图

    def create_app(config_name):
        # ...
    
        # 注册蓝本 main
        from .main import main as main_blueprint
        app.register_blueprint(main_blueprint, url_prefix='/main')
    
        # 注册蓝本 admin
        from .admin import admin as admin_blueprint
        app.register_blueprint(admin_blueprint, url_prefix='/admin')
    
        return app
    

    蓝图的错误处理(以蓝图main为例)

    app/main/errors.py

    
    # -*- coding:utf-8 -*-
    __author__ = '东方鹗'
    
    from flask import render_template, request, jsonify
    from . import main
    
    
    @main.app_errorhandler(403)
    def forbidden(e):
        if request.accept_mimetypes.accept_json and \
                not request.accept_mimetypes.accept_html:
            response = jsonify({'error': 'forbidden'})
            response.status_code = 403
            return response
        return render_template('main/errors/403.html'), 403
    
    
    @main.app_errorhandler(404)
    def page_not_found(e):
        if request.accept_mimetypes.accept_json and \
                not request.accept_mimetypes.accept_html:
            response = jsonify({'error': 'not found'})
            response.status_code = 404
            return response
        return render_template('main/errors/404.html'), 404
    
    
    @main.app_errorhandler(500)
    def internal_server_error(e):
        if request.accept_mimetypes.accept_json and \
                not request.accept_mimetypes.accept_html:
            response = jsonify({'error': 'internal server error'})
            response.status_code = 500
            return response
        return render_template('main/errors/500.html'), 500
    

    在蓝图中编写错误处理程序少有不同,如果使用errorhandler修时期,那么只有蓝图中的错误才能触发处理程序。要想注册程序全局的错误处理程序,必须使用app_errorhandler。当然,示例还提供基于json的错误提示编写方法。

    蓝图中定义程序路由(以蓝图main为例)

    app/main/views.py

    
    # -*- coding:utf-8 -*-
    __author__ = '东方鹗'
    
    from flask import render_template
    from . import main
    
    
    @main.route('/', methods=['GET', 'POST'])
    def index():
    
        return render_template('main/index.html')
    

    templates/main/index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>蓝图main欢迎您!</h1>
    </body>
    </html>
    

    在蓝图中编写视图函数主要有两点不同:第一,和前面的错误处理程序一样,路由修饰器由蓝图提供;第二,url_for()函数的用法不同。在蓝图中,Flask会为蓝图中的全部端点加上一个 ** 命名空间 ** ,这样就可以在不同的蓝图中使用相同的端点名定义视图函数,而不会产生冲突。命名空间就是蓝图的名字(Blueprint构造函数的第一个参数),所以视图函数index()注册的端点名是main.index,其URL使用url_for('main.index')获取。

    启动脚本

    manage.py

    # -*- coding:utf-8 -*-
    __author__ = '东方鹗'
    
    import os
    from app import create_app, db
    from flask_script import Manager, Shell
    # from flask_migrate import Migrate, MigrateCommand
    
    app = create_app(os.getenv('FLASK_CONFIG') or 'default')
    manager = Manager(app=app)
    # migrate = Migrate(app=app, db=db)
    
    
    def make_shell_context():
        return dict(app=app, db=db)
    
    manager.add_command("shell", Shell(make_context=make_shell_context))
    # manager.add_command('db', MigrateCommand)
    
    
    @manager.command
    def test():
        """ 单元测试 """
        import unittest
        tests = unittest.TestLoader().discover('tests')
        unittest.TextTestRunner(verbosity=2).run(test=tests)
    
    if __name__ == '__main__':
        manager.run()
    

    需求文件

    程序中必须包含一个requirements.txt文件,用于记录所有依赖包及其精确的版本号。如果要在另一台电脑上重新生成虚拟环境,这个文件的重要性就体现出来了,例如部署程序时使用的电脑。pip可以使用如下命令自动生成这个文件:

    (flask)$ pip freeze > requirements.txt
    

    安装或升级包后,最好更新这个文件。

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

    (flask)$ pip install -r requirements.txt
    

    如果在今后由于升级程序包遇到问题,你可以随时换回这个需求文件中的版本,因为这些版本和程序兼容。

    结果展示

    启动程序

    (flask)$ python manage.py runserver --host 0.0.0.0
    

    在浏览器中输入http://localhost:5000/main,显示如下内容。
    [图片上传失败...(image-8ff1f3-1509964243171)]

    OK,到此一个按照大型项目结构组织的项目已经完成了基础的构造。

    相关文章

      网友评论

        本文标题:第六章 实例项目的描述

        本文链接:https://www.haomeiwen.com/subject/hozlmxtx.html