美文网首页你应该会玩 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,到此一个按照大型项目结构组织的项目已经完成了基础的构造。

相关文章

  • 第六章 实例项目的描述

    尽管在单一脚本中编写小型WEB程序很方便,但是这种方法并不能广泛使用。程序变复杂之后,使用单个大型源码文件会导致后...

  • Array对象splice方法

    语法 返回值 类型 描述Array 包含被删除项目的新数组,如果有的话。 实例

  • Bug描述规范

    Bug描述规范 一、目的 清晰简单描述好缺陷,编写有效的缺陷实例,帮助开发人员有效的进行缺陷定位,重现错误,从而去...

  • “美女”特性描述实例

    亲爱的朋友们,昨天我说到跟小马学习如何整合群体人物的共性特征,他举了一个塑造“美女”形象的例子,说会赋予她一点点说...

  • 有目的的描述

    今日看到一段话,有关对形象的描述。 为了表示一个人很瘦弱。 他先是从环境渲染,从一个入寒的季节入手,用裤子的松垮,...

  • commons模块阅读8:MessageQueue

    说明 该类用于描述消息队列,相当于前面讲过的QueueData中的一项有多少个实例,取决于QueueData的属性...

  • 哨兵+性测基本指标简介(响应时间、TPS)+Ginder简介

    哨兵系统:(参考) 目的:实现对实例和基本监控项的监控 实际操作: 具体实现这里参考的是redis的哨兵监控 1、...

  • 面向对象(三)

    1、文字描述 类属性、实例属性的区别。 2、写代码创建类属性和实例属性并注释指明是类属性还是实例属性 3、文字描述...

  • 知识图谱:方法、实践与应用笔记-第5章 知识图谱融合

    概述 知识图谱包含描述抽象知识的本体层和描述具体事实的实例层。本体层用于描述特定领域中的抽象概念、属性、公理;实例...

  • 战国时代0922

    重读《中国史钢》目的了解王朝更迭、政治兴衰、社会变迁,也描述文化发展、商业经济,地域民俗、大众生活等。 第六章 战...

网友评论

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

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