改造原因

单个脚本文件

  • 优点:编写小型Web应用方便
  • 缺点:

    • 伸缩性差,应用复杂后,单个大型源码文件会出现众多问题
    • 应用在全局中创建,无法动态修改配置

      项目结构

      在命令行中执行tree > readme.md 可将目录结构写入readme.md文件
      1. blog
      2. ├─app
      3. ├─templates/
      4. ├─static/
      5. ├─main/
      6. ├─__init__.py
      7. ├─views.py
      8. ├─forms.py
      9. ├─errors.py
      10. ├─auth/
      11. ├─__init__.py
      12. ├─views.py
      13. ├─forms.py
      14. ├─__init__.py
      15. ├─email.py
      16. ├─models.py
      17. ├─migrations/
      18. ├─tests/
      19. ├─venv/
      20. ├─requirements.txt
      21. ├─config.py
      22. ├─run.py

      配置选项

      应用设置配置的方法

  • app.config对象

    1. app.config[''] == xxx
  • 配置类:需要导入配置模块

    1. class config_object:
    2. SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
    3. SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    4. app.config.from_object(config_object)
  • 文件方式

config.py

  1. DEBUG=True

run.py

  1. from flask import Flask
  2. app = Flask(__name__)
  3. # silent=true 文件不存在时不报错。默认为false
  4. app.config.from_pyfile("config.py", silent=True)

配置设置示例

config.py
Config中包含通用配置,各子类定义专用的配置
末尾config字典注册了不同环境的配置,并注册了一个默认配置

  1. import os
  2. basedir = os.path.abspath(os.path.dirname(__file__))
  3. class Config:
  4. SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
  5. SQLALCHEMY_COMMIT_ON_TEARDOWN = True
  6. FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
  7. FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>'
  8. FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
  9. @staticmethod
  10. def init_app(app):
  11. pass
  12. class DevelopmentConfig(Config):
  13. DEBUG = True
  14. MAIL_SERVER = 'smtp.googlemail.com'
  15. MAIL_PORT = 587
  16. MAIL_USE_TLS = True
  17. MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
  18. MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
  19. SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
  20. 'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
  21. config = {
  22. 'development': DevelopmentConfig,
  23. 'testing': TestingConfig,
  24. 'production': ProductionConfig,
  25. 'default': DevelopmentConfig
  26. }

app/init.py

  1. from config import config
  2. app.config.from_object(config['development'])

应用包

用来保存程序的所有代码、模板和静态文件。数据库模型和电子邮件支持函数也被移到了这个
包中,分别保存为 app/models.py 和 app/email.py

工厂函数

为解决动态修改配置,延迟创建应用实例,把创建过程移到可显式调用的工厂函数。这样可以给脚本留出配置应用的时间,还可以创建多实例。
工厂函数在app包构建文件中定义
app/init.py

  1. from flask import Flask
  2. from flask_bootstrap import Bootstrap
  3. from flask-mail import Mail
  4. bootstrap = Bootstrap()
  5. mail = Mail()
  6. def create_app(config_name):
  7. app = Flask(__name__)
  8. app.config.from_object(config_name)
  9. bootstrap.init_app(app)
  10. mail.init_app(app)
  11. return app

蓝本blueprint

转换成应用工厂函数后,定义路由变得复杂,现在应用在运行时创建,必须调用create_app()后才能使用app.route装饰器。flask提供蓝本解决该问题,蓝本定义的路由和错误程序处理休眠状态,直到注册到应用中,才成为应用一部分。使用位于全局作用域中的蓝本时,定义路由和错误处理程序的方法几乎与单脚本应用一样

创建蓝本

创建蓝本通过Blueprint类创建,需要传入两个参数,第一个为蓝本名称,第二个为蓝本所在包或者模块,通常使用python变量name即可
app/main/init.py

  1. from flask import Blueprint
  2. from . import view, errors
  3. blueprint = Blueprint("main", __name__)

注册蓝本

app/init.py

  1. def create_app(config_name):
  2. # ...
  3. from .main import blueprint as main_blueprint
  4. app.register(main_blueprint)
  5. return app

视图函数

app/main/errors.py
如果使用errorhandler装饰器,那么只有蓝本中的错误才能触发处理程序。要想注册应用全局的错误处理程序,必须使用app_errorhandler装饰器

  1. from flask import render_template
  2. from . import main
  3. @main.app_errorhandler(404)
  4. def page_not_found(e):
  5. return render_template('404.html'), 404
  6. @main.app_errorhandler(500)
  7. def internal_server_error(e):
  8. return render_template('500.html'), 500

app/main/views.py
在蓝本中编写视图函数主要有两点不同

  • 与前面的错误处理程序一样,路由装饰器由蓝本提供,因此使用的是main.route,而非app.route;
  • url for()函数的用法不同。url for()函数的第一个参数是路由的端点名,在应用的路由中,默认为视图函数的名称。例如,在单脚本应用中,index()视图函数的URL可使用url for(‘index’)获取。端点名url_for(“main.index”),url for()函数还支持一种简写的端点形式,在蓝本中可以省略蓝本名,例如url for(‘.index’) ```python from flask import render_template, redirct, url_for from . import main from .forms import NameForm

@main.route(‘/‘, methods=[‘GET’, ‘POST’]) def index(): form = NameForm() if form.validate_on_submit(): return redirect(url_for(‘.index’))
return render_template(‘index.html’,form=form, name=session.get(‘name’))

  1. <a name="LJymk"></a>
  2. # 启动文件
  3. run.py
  4. ```python
  5. from app import create_app
  6. app = create_app('default')

文件启动

  1. (venv) $ set flask_app=run.py
  2. (venv) $ flask run

需求文件

程序中必须包含一个 requirements.txt 文件,用于记录所有依赖包及其精确的版本号。在其他电脑重新生成虚拟环境,看根据该文件快速安装依赖包

生成\更新需求文件

  1. (venv) $ pip freeze >requirements.txt

根据需求文件安装模块

  1. (venv) $ pip install -r requirements.txt

安装指定版本及升级

  1. (venv) $ pip install package==version
  2. (venv) $ pip install --upgrade xxx

单元测试

创建数据库