7. Flask 大型程序的结构

7.1 项目结构

该结构是根据<<FlaskWeb开发:基于Python的Web应用开发实战>> 第7章中推荐的项目结构搭建,是一种使用包和模块组织大型程序的方式。

这种结构有4个顶级的文件夹:

  • Flask 主程序(功能业务代码)一般都保存在app包中;
  • migrations 文件夹包含数据库迁移脚本;
  • 单元测试编写在test包中
  • venv文件夹包含Python虚拟环境

同时还创建了一些新文件:

  • requirements.txt 列出了所有的依赖包,便于在其他业务环境中生成相同的虚拟环境;
  • config.py 由于存储配置
  • manage.py 用于启动程序及其他的程序任务(本例中加入了Shell,MigrateCommand)

7.2 配置选项

我们给程序设定了多个配置,开发,测试,生产环境。

config.py

 1 import os
 2 
 3 basedir = os.path.abspath(os.path.dirname(__file__))
 4 
 5 
 6 class Config:
 7     SECRET_KEY = os.environ.get('SECRET_KEY') or 'Hard to guess string!'
 8     SQLALCHEMY_COMMIT_ON_TEARDOWN = True
 9     SQLALCHEMY_TRACK_MODIFICATIONS = True
10     FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
11     FLASKY_MAIL_SENDER = '[email protected]'
12     FLASK_ADMIN = os.environ.get('FLASKY_ADMIN')
13 
14     @staticmethod
15     def init_app(app):
16         pass
17 
18 
19 class DevelopmentConfig(Config):
20     DEBUG = True
21     MAIL_SERVER = 'mail.9158.com'
22     MAIL_PORT = 587
23     MAIL_USE_TLS = True
24     MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
25     MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
26     SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URI') or 'sqlite:///' + os.path.join(basedir,
27                                                                                                 'data-dev.sqlite')
28 
29 
30 class TestingConfig(Config):
31     TESTING = True
32     SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URI') or 'sqlite:///' + os.path.join(basedir,
33                                                                                                  'data-test.sqlite')
34 
35 
36 class ProductionConfig(Config):
37     SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URI') or 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
38 
39 
40 config = {
41     'development': DevelopmentConfig,
42     'testing': TestingConfig,
43     'production': ProductionConfig,
44     'default': DevelopmentConfig
45 }

基类Config包含通用配置,子类分别定义专用的配置。

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

在配置最后,config字典还注册了不同的环境。

7.3 程序包

程序包用来保存所有的代码,模板和静态文件。我们可以把这个包直接成为app,如果有需求也可以使用专有的名字;templates,static,models.py,mail.py都是这个包的一部分。

7.3.1 使用程序工厂函数

app/__init__.py

 1 from flask import Flask, render_template
 2 from flask_bootstrap import Bootstrap
 3 from flask_mail import Mail
 4 from flask_moment import Moment
 5 from flask_sqlalchemy import SQLAlchemy
 6 from config import config
 7 
 8 bootstrap = Bootstrap()
 9 mail = Mail()
10 moment = Moment()
11 db = SQLAlchemy()
12 
13 
14 def create_app(config_name):
15     app = Flask(__name__)
16     app.config.from_object(config[config_name])
17     config[config_name].init_app(app)
18 
19     bootstrap.init_app(app)
20     mail.init_app(app)
21     moment.init_app(app)
22     db.init_app(app)
23 
24     from .main import main as main_blueprint
25     app.register_blueprint(main_blueprint)
26     return app

构造文件导入了大多数正在使用的Flask扩展,。由于尚未初始化所需的程序实例,所以没有初始化拓展,创建扩展类时没有传入参数。create_app函数就是程序的工厂函数,接收一个参数,是程序使用的配置名。程序的配置在config.py中定义,可直接使用Flask app.config中提供的from_object()方法直接导入程序。

7.3.2 使用蓝本实现程序功能

蓝本和程序类似,也可以定义路由。不同的是,在蓝本中定义的路由处于休眠状态,直到蓝本被注册到程序上面,录有才真正成为程序的一部分。

创建蓝本:

app/main/__init__.py

from flask import Blueprint


main = Blueprint('main', __name__)


from . import views, errors

注册蓝本:

app/__init__.py

def create_app(config_name):
  ......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

蓝本中定义的程序路由:

app/main/views.py

 1 from datetime import datetime
 2 from flask import render_template, session, redirect, url_for, flash
 3 
 4 from . import main
 5 from .forms import NameForm
 6 from .. import db
 7 from ..models import User
 8 
 9 
10 @main.route('/', methods = ['GET', 'POST'])
11 def index():
12     form = NameForm()
13     if form.validate_on_submit():
14         user = User.query.filter_by(username=form.name.data).first()
15         if user is None:
16             user = User(username=form.name.data)
17             db.session.add(user)
18             session['known'] = False
19         else:
20             session['known'] = True
21         old_name = session.get('name')
22         if old_name is not None and old_name != form.name.data:
23             flash('Look like you have changed your name!')
24         session['name'] = form.name.data
25         form.name.data = ''
26         return redirect(url_for('.index'))
27     all_user = User.query.all()
28     return render_template('index.html', form=form, name=session.get('name'), known=session.get('known', False),
29                            current_time=datetime.utcnow(), user_list=all_user)

在蓝本中编写视图函数主要有两点不同:第一,和前面错误处理程序一样,路由装饰器由蓝本提供;第二url_for函数的用法不同,url_for('.index')。

表单对象也移到了蓝本中

app/main/forms.py

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired


class NameForm(FlaskForm):
    name = StringField('What is your name?', validators=[DataRequired()])
    submit = SubmitField('Submit')

7.4 启动脚本

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

manage.py

 1 import os
 2 from app import create_app, db
 3 from app.models import User, Role
 4 from flask_script import Manager, Shell
 5 from flask_migrate import Migrate, MigrateCommand
 6 
 7 app = create_app(os.getenv('FLASK_CONFIG') or 'default')
 8 manager = Manager(app)
 9 migrate = Migrate(app, db)
10 
11 
12 def make_shell_context():
13     return dict(app=app, db=db, User=User, Role=Role)
14 
15 
16 manager.add_command("shell", Shell(make_shell_context))
17 manager.add_command("db", MigrateCommand)
18 
19 if __name__ == '__main__':
20     manager.run()

这个脚本先创建程序,如果已经定义了环境变量FLASK_CONFIG,则从变量中读取,否则使用'default'配置。

7.5 需求文件

程序中必须包含一个requirements.txt文件,用于记录程序所依赖的包和包的版本号,pip可以使用如下命令生产这个文件:

(venv) pip freeze > requirements.txt

如果要在新机器上传创建这个一样的虚拟环境,可运行以下命令。

(venv)pip install -r requirements.txt

alembic==0.9.9
blinker==1.4
click==6.7
dominate==2.3.1
Flask==0.12.2
Flask-Bootstrap==3.3.7.1
Flask-Mail==0.9.1
Flask-Migrate==2.1.1
Flask-Moment==0.6.0
Flask-Script==2.0.6
Flask-SQLAlchemy==2.3.2
Flask-WTF==0.14.2
itsdangerous==0.24
Jinja2==2.10
Mako==1.0.7
MarkupSafe==1.0
python-dateutil==2.7.2
python-editor==1.0.3
six==1.11.0
SQLAlchemy==1.2.7
visitor==0.1.3
Werkzeug==0.14.1
WTForms==2.1

猜你喜欢

转载自www.cnblogs.com/LouisZJ/p/8932392.html