我的编程空间,编程开发者的网络收藏夹
学习永远不晚

使用Python的Flask框架构建大型Web应用程序的结构示例

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

使用Python的Flask框架构建大型Web应用程序的结构示例

虽然小型web应用程序用单个脚本可以很方便,但这种方法却不能很好地扩展。随着应用变得复杂,在单个大的源文件中处理会变得问题重重。

与大多数其他web框架不同,Flask对大型项目没有特定的组织方式;应用程序的结构完全交给开发人员自己决定。在这一章,提出一个可能的方式来组织管理一个大型应用程序的包和模块。这种结构将用于书中其余的示例中。

1、项目结构

示例 基本多文件Flask应用结构


|-flasky
 |-app/
  |-templates/
  |-static/
  |-main/
   |-__init__.py
   |-errors.py
   |-forms.py
   |-views.py
  |-__init__.py
  |-email.py
  |-models.py
 |-migrations/
 |-tests/
  |-__init__.py
  |-test*.py
 |-venv/
 |-requirements.txt
 |-config.py
 |-manage.py

这个结构有四个顶层目录:

Flask应用一般放置在名为app的目录下。 migrations目录包含数据库迁移脚本,这和之前说的一样。 单元测试放置在test目录下 venv目录包含Python虚拟环境,这和之前说的也是一样的。

还有一些新的文件:

requirements.txt列出一些依赖包,这样就可以很容易的在不同的计算机上部署一个相同的虚拟环境。 config.py存储了一些配置设置。 manage.py用于启动应用程序和其他应用程序任务。

为了帮助你完全理解这个结构,下面会描述将hello.py应用改为符合这一结构的整个流程。

2、配置选项
应用程序通常需要几个配置设置。最好的例子就是在开发过程中需要使用不同的数据库,测试,生产环境,这样他们可以做到互不干扰。

我们可以使用配置类的层次结构来代替hello.py中的简单类字典结构配置。下面展示了config.py文件。

config.py:应用程序配置


import os
basedir = os.path.abspath(os.path.dirname(__file__))

class Config:
  SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string' 
  SQLALCHEMY_COMMIT_ON_TEARDOWN = True
  FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
  FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>' 
  FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
  
  @staticmethod
  def init_app(app): 
    pass

class DevelopmentConfig(Config): 
  DEBUG = True

  MAIL_SERVER = 'smtp.googlemail.com'
  MAIL_PORT = 587
  MAIL_USE_TLS = True
  MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
  MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD') 
  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,由于它的敏感性,可以在环境中设置,但如果环境中没有定义就必须提供一个默认值。

在三个配置中SQLALCHEMY_DATABASE_URI变量可以分配不同的值。这样应用程序可以在不同的配置下运行,每个可以使用不同的数据库。

配置类可以定义一个将应用程序实例作为参数的init_app()静态方法。这里特定于配置的初始化是可以执行的。这里Config基类实现一个空init_app()方法。

在配置脚本的底部,这些不同的配置是注册在配置字典中。将其中一个配置(开发配置)注册为默认配置。

3、应用程序包
应用程序包放置了所有应用程序代码、模板和静态文件。它被简单的称为app,也可以给定一个特定于应用的名称(如果需要的话)。templates和static目录是应用的一部分,因此这两个目录应该放置在app中。数据库模型和电子邮件支持功能也要置入到这个包中,每个都以app/models.py和app/email.py形式存入自己的模块当中。

3.1、使用一个应用程序工厂

在单个文件中创建应用程序的方式非常方便,但是它有一个大缺点。因为应用程序创建在全局范围,没有办法动态的适应应用配置的更改:脚本运行时,应用程序实例已经创建,所以它已经来不及更改配置。对于单元测试这是特别重要的,因为有时需要在不同的配置下运行应用程序来获得更好的测试覆盖率。

解决这一问题的方法就是将应用程序放入一个工厂函数中来延迟创建,这样就可以从脚本中显式的调用。

这不仅给脚本充足的时间来设置配置,也能用于创建多个应用程序实例——一些在测试过程中非常有用的东西。被定义在app包的构造函数中的应用程序工厂函数会在示例7-3中展示。

这个构造函数导入大部分当前需要使用的扩展,但因为没有应用程序实例初始化它们,它可以被创建但不初始化通过不传递参数给它们的构造函数。create_app()即应用程序工厂函数,需要传入用于应用程序的配置名。配置中的设置被保存在config.py中的一个类中,可以使用Flask的app.config配置对象的from_object()方法来直接导入。配置对象可以通过对象名从config字典中选出。一旦应用程序被创建且配置好,扩展就可以被初始化。调用扩展里的init_app()之前先创建并完成初始化工作。

app/ _init__.py:应用程序包构造函数_


from flask import Flask, render_template 
from flask.ext.bootstrap import Bootstrap 
from flask.ext.mail import Mail
from flask.ext.moment import Moment
from flask.ext.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)

  # attach routes and custom error pages here
  
  return app

工厂函数返回创建的应用程序实例,但是请注意,在当前状态下使用工厂函数创建的应用程序是不完整的,因为它们没有路由和自定义错误页面处理程序。这是下一节的主题。

3.2、在蓝图中实现应用程序的功能

应用程序工厂的转化工作引出了路由的复杂化。在单脚本应用中,应用程序实例是全局的,所以可以很容易地使用app.route装饰器定义路由。但是现在应用程序在运行时创建,app.route装饰器只有在create_app()调用后才开始存在,这就太迟了。就像路由那样,这些通过app.errorhandler装饰器定义的自定义错误页面处理程序也存在同样的问题。

幸运的是Flask使用蓝图来提供一个更好的解决方案。一个蓝图就类似于一个可以定义路由的应用程序。不同的是,和路由相关联的蓝图都在休眠状态,只有当蓝图在应用中被注册后,此时的路由才会成为它的一部分。使用定义在全局作用域下的蓝图,定义应用程序的路由就几乎可以和单脚本应用程序一样简单了。

和应用程序一样,蓝图可以定义在一个文件或一个包中与多个模块一起创建更结构化的方式。为了追求最大的灵活性,可以在应用程序包中创建子包来持有蓝图。下面展示了创建蓝图的构造函数。

app/main/ _init__.py:创建蓝图_


from flask import Blueprint

main = Blueprint('main', __name__) 

from . import views, errors

蓝图是通过实例化Blueprint类对象来创建的。这个类的构造函数接收两个参数:蓝图名和蓝图所在的模块或包的位置。与应用程序一样,在大多数情况下,对于第二个参数值使用Python的__name__变量是正确的。

应用程序的路由都保存在app/main/views.py模块内部,而错误处理程序则保存在app/main/errors.py中。导入这些模块可以使路由、错误处理与蓝图相关联。重要的是要注意,在app/init.py脚本的底部导入模块要避免循环依赖,因为view.py和errors.py都需要导入main蓝图。

蓝图和应用程序一样注册在create_app()工厂函数中,如下所示。

示例 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

在蓝图中写错误处理的不同之处是,如果使用了errorhandler装饰器,则只会调用在蓝图中引起的错误处理。而应用程序范围内的错误处理则必须使用app_errorhandler。

这里展示了被更新在蓝图中的应用程序路由。

app/main/views.py:带有蓝图的应用程序路由


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():
    # ...
    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())

在蓝图中写视图函数有两大不同点。第一,正如之前的错误处理一样,路由装饰器来自于蓝图。第二个不同是url_for()函数的使用。你可能会回想,该函数的第一个参数为路由节点名,它给基于应用程序的路由指定默认视图函数。例如,单脚本应用程序中的index()视图函数的URL可以通过url_for('index')来获得。

不同的是Flask名称空间适用于来自蓝图的所有节点,这样多个蓝图可以使用相同节点定义视图函数而不会产生冲突。名称空间就是蓝图名(Blueprint构造函数中的第一个参数),所以index()视图函数注册为main.index且它的URL可以通过url_for('main.index')获得。

在蓝图中,url_for()函数同样支持更短格式的节点,省略蓝图名,例如url_for('.index')。有了这个,就可以这样使用当前请求的蓝图了。这实际意味着相同蓝图内的重定向可以使用更短的形式,如果重定向跨蓝图则必须使用带名称空间的节点名。

完成了应用程序页面更改,表单对象也保存在app/main/forms.py模块中的蓝图里面。

4、启动脚本
顶层目录中的manage.py文件用于启动应用。

manage.py:启动脚本


#!/usr/bin/env python
import os
from app import create_app, db
from app.models import User, Role
from flask.ext.script import Manager, Shell
from flask.ext.migrate import Migrate, MigrateCommand

app = create_app(os.getenv('FLASK_CONFIG') or '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()

这个脚本开始于创建应用程序。使用环境变量FLASK_CONFIG,若它已经定义了则从中获取配置;如果没有,则是用默认配置。然后用于Python shell的Flask-Script、Flask-Migrate以及自定义上下文会被初始化。

为了方便,会增加一行执行环境,这样在基于Unix的操作系统上可以通过./manage.py来执行脚本来替代冗长的python manage.py。

5、需求文件
应用程序必须包含requirements.txt文件来记录所有依赖包,包括精确的版本号。这很重要,因为可以在不同的机器上重新生成虚拟环境,例如在生产环境的机器上部署应用程序。这个文件可以通过下面的pip命令自动生成:


(venv) $ pip freeze >requirements.txt

当安装或更新一个包之后最好再更新一下这个文件。以下展示了一个需求文件示例:


Flask==0.10.1
Flask-Bootstrap==3.0.3.1
Flask-Mail==0.9.0
Flask-Migrate==1.1.0
Flask-Moment==0.2.0
Flask-SQLAlchemy==1.0
Flask-Script==0.6.6
Flask-WTF==0.9.4
Jinja2==2.7.1
Mako==0.9.1
MarkupSafe==0.18
SQLAlchemy==0.8.4
WTForms==1.0.5
Werkzeug==0.9.4
alembic==0.6.2
blinker==1.3
itsdangerous==0.23

当你需要完美复制一个虚拟环境的时候,你可以运行以下命令创建一个新的虚拟环境:


(venv) $ pip install -r requirements.txt

当你读到这时,示例requirements.txt文件中的版本号可能已经过时了。如果喜欢你可以尝试用最近发布的包。如果遇到任何问题,你可以随时回退到需求文件中与应用兼容的指定版本。

6、单元测试
这个应用非常小以至于不需要太多的测试,但是作为示例会在示例中展示两个简单的测试定义。

示例: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'])

编写好的测试使用的是来自于Python标准库中标准的unittest包。setUp()和tearDown()方法在每个测试之前和之后运行,且任何一个方法必须以test_开头作为测试来执行。

建议:如果你想要学习更多使用Python的unittest包来写单元测试的内容,请参阅官方文档。
setUp()方法尝试创建一个测试环境,类似于运行应用程序。首先它创建应用程序配置用于测试并激活上下文。这一步确保测试可以和常规请求一样访问current_app。然后,当需要的时候,可以创建一个供测试使用的全新数据库。数据库和应用程序上下文会在tearDown()方法中被移除。

第一个测试确保应用程序实例存在。第二个测试确保应用程序在测试配置下运行。为了确保tests目录有效,需要在tests目录下增加__init__.py文件,不过该文件可以为空,这样unittest包可以扫描所有模块并定位测试。

建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 7a来切换到这个版本的应用程序。为了确保你已经安装了所有依赖集,需要运行pip install -r requirements.txt。
为了运行单元测试,可以在manage.py脚本中增加一个自定义的命令。

下面展示如何添加测试命令。

示例:manage.pyt:单元测试启动脚本


@manager.command
def test():
  """Run the unit tests."""
  import unittest
  tests = unittest.TestLoader().discover('tests') 
  unittest.TextTestRunner(verbosity=2).run(tests)

manager.command装饰器使得它可以很容易的实现自定义命令。被装饰的函数名可以被当做命令名使用,且函数的文档字符串会显示帮助信息。test()函数的执行会调用unittest包中的测试运行器。

单元测试可以像下面这样执行:


(venv) $ python manage.py test

test_app_exists (test_basics.BasicsTestCase) ... ok
test_app_is_testing (test_basics.BasicsTestCase) ... ok

.----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

7、数据库启动
与单脚本的应用相比,重构后的应用使用不同数据库。

从环境变量中获取的数据库URL作为首选,默认SQLite数据库作为可选。三个配置中的环境变量和SQLite数据库文件名是不一样的。例如,开发配置的URL是从DEV_DATABASE_URL环境变量中获取,如果没有定义则会使用名为data-dev.sqlite的SQLite数据库。

无论数据库URL源的是哪一个,都必须为新的数据库创建数据库表。如果使用了Flask-Migrate来保持迁移跟踪,数据库表可以被创建或更新到最近的版本通过下面的命令:


(venv) $ python manage.py db upgrade

相信与否,已经到了第一部分结束的地方。你现在已经学到了Flask必要的基本要素,但是你不确定如何将这些零散的知识组合在一起形成一个真正的应用程序。第二部分的目的是通过开发一个完整的应用程序来带领你继续前行。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

使用Python的Flask框架构建大型Web应用程序的结构示例

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

使用Python的Flask框架构建大型Web应用程序的结构示例

虽然小型web应用程序用单个脚本可以很方便,但这种方法却不能很好地扩展。随着应用变得复杂,在单个大的源文件中处理会变得问题重重。 与大多数其他web框架不同,Flask对大型项目没有特定的组织方式;应用程序的结构完全交给开发人员自己决定。在
2022-06-04

如何使用Flask框架构建URL缩短器Web应用程序

使用Flask和Python构建URL缩短器是学习如何使用Flask构建Web应用程序的好方法。可以使用在这一教程中学习的技能制作更复杂和高级的应用程序。
Flask框架Web2024-11-30

使用Python的Flask框架来搭建第一个Web应用程序

1、初始化 在这章,你将学到Flask应用程序的不同部分。同时,你将编写和运行你的第一个Flask web应用程序。 所有的Flask应用程序都必须创建一个 应用程序实例 。使用web服务器网关接口协议将所有从客户端接收的请求传递给这个对象
2022-06-04

五个用于构建Web应用程序的Go Web框架

就这样!您已经安装了Buffalo框架,创建了一个简单的“Hello, World!” Buffalo项目,并以开发模式运行了该应用程序。Buffalo具有许多功能和项目结构,可以帮助您高效地构建Web应用程序。您现在可以通过定义路由、控制

Node.js Koa:构建现代Web应用程序的框架

Koa 是一款简洁、高效的 Node.js Web 框架,构建在 Express 的基础上,旨在提供更强大、更灵活的 Web 开发体验。本文将介绍 Koa 的基本概念及其使用方式,帮助您轻松构建现代 Web 应用程序。
Node.js Koa:构建现代Web应用程序的框架
2024-02-08

基于Flask的Web应用程序插件式结构

事实上,很多应用程序基于插件式结构开发,可以很方便了扩展软件的功能,并且这些功能完全可以依托于第三方开发者,只要提供好接口和完备文档,比如wordpress、谷歌火狐浏览器等。Python这样的动态语言天生就支持插件式编程。与C++相比,P
2023-01-30

Mesop:快速构建 Web 应用的 Python UI 框架

Mesop是一款非常适合快速原型开发和内部应用开发的框架,它以Python为基础,减轻了开发者的学习负担并提高了工作效率。

架构之美:使用 Nest.js 构建强大的 Node.js 应用程序

Nest.js:构建强大 Node.js 应用程序的架构之美
架构之美:使用 Nest.js 构建强大的 Node.js 应用程序
2024-04-03

用 PHP 框架构建企业级应用程序的架构设计

php框架架构设计对于构建企业级应用程序至关重要。mvc、分层和微服务架构是三种常见模式:mvc(模型-视图-控制器)模式分离应用程序的业务逻辑、用户界面和交互。分层架构将应用程序划分为数据访问层、业务逻辑层和表示层,提高可扩展性和模块性。
用 PHP 框架构建企业级应用程序的架构设计
2024-05-24

PHP MVC 架构:构建面向未来的 Web 应用程序

PHP MVC 架构是一种强大的设计模式,使开发人员能够构建可维护、可扩展且面向未来的 Web 应用程序。它通过分离应用程序的各个方面(模型、视图、控制器)来实现代码解耦和职责分离。
PHP MVC 架构:构建面向未来的 Web 应用程序
2024-03-02

前端开发:构建 Web 应用程序的前10个 JavaScript 框架

多年来,业界已经发布了大量 JavaScript 框架,怎样进行选择可能是一个挑战。如果你感到困惑,不知道应该选哪个或者究竟哪个适合你,那么我已经帮你解决了问题。在本文中,web前端小编将列出用来构建 Web 应用程序的前10个 JavaS
2023-06-03

使用Python的Twisted框架构建非阻塞下载程序的实例教程

第一个twisted支持的诗歌服务器 尽管Twisted大多数情况下用来写服务器代码,但为了一开始尽量从简单处着手,我们首先从简单的客户端讲起。 让我们来试试使用Twisted的客户端。源码在twisted-client-1/get-poe
2022-06-04

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录