一、目录结构html
目录:python
Flask程序通常保存在名为app的包中sql
migrations文件夹包含数据库迁移脚本shell
单元测试编写在test包中数据库
venv文件夹包含python虚拟环境flask
文件:bootstrap
requirements.txt 列出来了全部依赖包,便于在其余电脑中从新生成相同的虚拟环境session
config.py 存储配置app
manage.py 用于启动程序以及其余的程序任务函数
二、配置选项
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字典中注册了不一样的配置环境,并且还注册了一个默认的配置(本例的开发环境)
三、程序包
程序包用来保存程序的全部代码、模板和静态文件。能够直接称为app,若有需求,也可以使用专用名称。templates和static文件夹是程序包的一部分,数据库模型和电子邮件支持含函数也被移到这个包中
本例中将原先templates下的文件转移到app/templates下
四、使用程序工厂函数
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
五、在蓝本中实现程序功能
直接使用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
六、注册蓝本
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())
七、启动脚本
顶级问价夹中的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()
八、需求文件
更新需求文件
pip freeze >requirements.txt
九、单元测试
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'])
十、启动单元测试
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()
十一、运行单元测试
python manage.py test
不知道咋回事,头疼,待查
十二、建立数据库
python manage.py db upgrade