Flask从入门到精通之大型程序的结构一
尽管在单一脚本中编写小型Web 程序很方便,但这种方法并不能广泛使用。程序变复杂后,使用单个大型源码文件会导致很多问题。不同于大多数其他的Web 框架,Flask 并不强制要求大型项目使用特定的组织方式,程序结构的组织方式完全由开发者决定。在本节,我们将介绍一种使用包和模块组织大型程序的方式。
一.项目结构
Flask 程序的基本结构如下所示:
|-blogs
|-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
这种结构有4 个顶级文件夹:
- Flask程序一般都保存在名为app 的包中
- 和之前一样,migrations文件夹包含数据库迁移脚本
- 单元测试编写在tests包中
- 和之前一样,venv 文件夹包含Python 虚拟环境
同时还创建了一些新文件:
- requirements.txt列出了所有依赖包,便于在其他电脑中重新生成相同的虚拟环境
- config.py 存储配置
- manage.py用于启动程序以及其他的程序任务
为了帮助你完全理解这个结构,下面几节讲解把前面介绍的hello.py 程序转换成这种结构的过程
二.配置选项
程序经常需要设定多个配置。这方面最好的例子就是开发、测试和生产环境要使用不同的数据库,这样才不会彼此影响。我们不再使用hello.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'
MAIL_SERVER = os.environ.get('MAIL_SERVER', 'smtp.googlemail.com')
MAIL_PORT = int(os.environ.get('MAIL_PORT', ''))
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS', 'true').lower() in \
['true', 'on', '']
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>'
FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
SSL_REDIRECT = False
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_RECORD_QUERIES = True
FLASKY_POSTS_PER_PAGE =
FLASKY_FOLLOWERS_PER_PAGE =
FLASKY_COMMENTS_PER_PAGE =
FLASKY_SLOW_DB_QUERY_TIME = 0.5 @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://'
WTF_CSRF_ENABLED = False class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data.sqlite') @classmethod
def init_app(cls, app):
Config.init_app(app) # email errors to the administrators
import logging
from logging.handlers import SMTPHandler
credentials = None
secure = None
if getattr(cls, 'MAIL_USERNAME', None) is not None:
credentials = (cls.MAIL_USERNAME, cls.MAIL_PASSWORD)
if getattr(cls, 'MAIL_USE_TLS', None):
secure = ()
mail_handler = SMTPHandler(
mailhost=(cls.MAIL_SERVER, cls.MAIL_PORT),
fromaddr=cls.FLASKY_MAIL_SENDER,
toaddrs=[cls.FLASKY_ADMIN],
subject=cls.FLASKY_MAIL_SUBJECT_PREFIX + ' Application Error',
credentials=credentials,
secure=secure)
mail_handler.setLevel(logging.ERROR)
app.logger.addHandler(mail_handler) class HerokuConfig(ProductionConfig):
SSL_REDIRECT = True if os.environ.get('DYNO') else False @classmethod
def init_app(cls, app):
ProductionConfig.init_app(app) # handle reverse proxy server headers
from werkzeug.contrib.fixers import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app) # log to stderr
import logging
from logging import StreamHandler
file_handler = StreamHandler()
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler) class DockerConfig(ProductionConfig):
@classmethod
def init_app(cls, app):
ProductionConfig.init_app(app) # log to stderr
import logging
from logging import StreamHandler
file_handler = StreamHandler()
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler) class UnixConfig(ProductionConfig):
@classmethod
def init_app(cls, app):
ProductionConfig.init_app(app) # log to syslog
import logging
from logging.handlers import SysLogHandler
syslog_handler = SysLogHandler()
syslog_handler.setLevel(logging.INFO)
app.logger.addHandler(syslog_handler) config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'heroku': HerokuConfig,
'docker': DockerConfig,
'unix': UnixConfig, 'default': DevelopmentConfig
}
基类Config 中包含通用配置,子类分别定义专用的配置。如果需要,你还可添加其他配置类。为了让配置方式更灵活且更安全,某些配置可以从环境变量中导入。例如,SECRET_KEY 的值,这是个敏感信息,可以在环境中设定,但系统也提供了一个默认值,以防环境中没有定义。在3 个子类中,SQLALCHEMY_DATABASE_URI 变量都被指定了不同的值。这样程序就可在不同的配置环境中运行,每个环境都使用不同的数据库。配置类可以定义init_app() 类方法,其参数是程序实例。在这个方法中,可以执行对当前环境的配置初始化。现在,基类Config 中的init_app() 方法为空。在这个配置脚本末尾,config 字典中注册了不同的配置环境,而且还注册了一个默认配置。
三.启动脚本
顶级文件夹下的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,则从中读取配置名;否则使用默认配置。然后初始化Flask-Script、Flask-Migrate 和为Python shell 定义的上下文。出于便利,脚本中加入了shebang 声明,所以在基于Unix 的操作系统中可以通过./manage.py 执行脚本,而不用使用复杂的python manage.py。
四.需求文件
程序中必须包含一个requirements.txt 文件,用于记录所有依赖包及其精确的版本号。如果要在另一台电脑上重新生成虚拟环境,这个文件的重要性就体现出来了,例如部署程序时使用的电脑。pip 可以使用如下命令自动生成这个文件:
pip freeze >requirements.txt
安装或升级包后,最好更新这个文件。需求文件的内容示例如下:
alembic==0.9.
bleach==2.0.
blinker==1.4
click==6.7
dominate==2.3.
Flask==0.12.
Flask-Bootstrap==3.3.7.1
Flask-HTTPAuth==3.2.
Flask-Login==0.4.
Flask-Mail==0.9.
Flask-Migrate==2.0.
Flask-Moment==0.5.
Flask-PageDown==0.2.
Flask-SQLAlchemy==2.2
Flask-WTF==0.14.
html5lib==0.999999999
itsdangerous==0.24
Jinja2==2.9.
Mako==1.0.
Markdown==2.6.
MarkupSafe==1.0
python-dateutil==2.6.
python-dotenv==0.6.
python-editor==1.0.
six==1.10.
SQLAlchemy==1.1.
visitor==0.1.
webencodings==0.5.
Werkzeug==0.12.
WTForms==2.1
如果你要创建这个虚拟环境的完全副本,可以创建一个新的虚拟环境,并在其上运行以下命令:
pip install -r requirements.txt
五.创建数据库
不管从哪里获取数据库URL,都要在新数据库中创建数据表。如果使用Flask-Migrate 跟踪迁移,可使用如下命令创建数据表或者升级到最新修订版本
python manage.py db upgrade
Flask从入门到精通之大型程序的结构一的更多相关文章
- Flask从入门到精通之大型程序的结构二
一.程序包 程序包用来保存程序的所有代码.模板和静态文件.我们可以把这个包直接称为app(应用),如果有需求,也可使用一个程序专用名字.templates 和static 文件夹是程序包的一部分,因此 ...
- Flask从入门到精通之flask程序入门
初始化 所有Flask程序都必须创建一个程序实例,Web服务器使用一种名为Web服务器网关接口的的协议(WSGI),把接收自客户端的所有请求转发给这个对象处理.程序实例是Flask类的对象,使用下面代 ...
- [ Python ] Flask 基于 Web开发 大型程序的结构实例解析
作为一个编程入门新手,Flask是我接触到的第一个Web框架.想要深入学习,就从<FlaskWeb开发:基于Python的Web应用开发实战>这本书入手,本书由于是翻译过来的中文版,理解起 ...
- Flask从入门到精通之Flask-Bootstrap的使用
Bootstrap(http://getbootstrap.com/)是Twitter 开发的一个开源框架,它提供的用户界面组件可用于创建整洁且具有吸引力的网页,而且这些网页还能兼容所有现代Web 浏 ...
- Flask从入门到精通之使用Flask-Migrate实现数据库迁移
在开发程序的过程中,你会发现有时需要修改数据库模型,而且修改之后还需要更新数据库.仅当数据库表不存在时,Flask-SQLAlchemy 才会根据模型进行创建.因此,更新表的唯一方式就是先删除旧表,不 ...
- Flask从入门到精通之使用Flask-SQLAlchemy管理数据库
Flask-SQLAlchemy 是一个Flask 扩展,简化了在Flask 程序中使用SQLAlchemy 的操作.SQLAlchemy 是一个很强大的关系型数据库框架,支持多种数据库后台.SQLA ...
- Flask从入门到精通之Flash消息
请求完成后,有时需要让用户知道状态发生了变化.这里可以使用确认消息.警告或者错误提醒.一个典型例子是,用户提交了有一项错误的登录表单后,服务器发回的响应重新渲染了登录表单,并在表单上面显示一个消息,提 ...
- Flask从入门到精通之重定向和用户会话
最新版的hello.py 存在一个可用性问题.用户输入名字后提交表单,然后点击浏览器的刷新按钮,会看到一个莫名其妙的警告,要求在再次提交表单之前进行确认.之所以出现这种情况,是因为刷新页面时浏览器会重 ...
- Flask从入门到精通之跨站请求伪造保护
默认情况下,Flask-WTF 能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,CSRF)的攻击.恶意网站把请求发送到被攻击者已登录的其他网站时就会引发CSRF ...
随机推荐
- linux 操作笔记
1.设置防火墙,允许用户使用http访问本机 [root@localhost geoserver]# systemctl enable httpdCreated symlink from /etc/s ...
- 现代编译原理--第二章(语法分析之LR(1))
(转载请表明出处 http://www.cnblogs.com/BlackWalnut/p/4472772.html) 前面已经介绍过LL(1),以及如何使用LL(1)文法.但是LL(K)文法要求在 ...
- 品味性能之道<四>:管理重于技术
一.性能优化中的角色分工 (1).老外的角色分工 在oracle性能优化方法论中,将IT系统中不同角色需要承担的性能优化工作罗列如下. 各司其职的角色分工 业务分析人员 1.业务需 ...
- const与预处理宏#define的区别
在c语言程序设计时,预处理器可以不受限制地建立宏并用它来替代值.因为预处理器只做一些文本替换,宏没有类型检测概念,也没有类型检测功能.所以预处理器的值替换会出现一些小的问题,出现的这些问题,在c++中 ...
- hdu-1141
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1141 参考文章:https://blog.csdn.net/fei____fei/article/de ...
- web service 项目 和 普通 web项目 的 区别
web service 面向的是开发者(需要再次开发) 普通web 面向的是用户(直接使用)
- Android ContentProvider和Uri详解
一.使用ContentProvider(内容提供者)共享数据 ContentProvider在android中的作用是对外共享数据, 也就是说你可以通过ContentProvider把应用中的数据共享 ...
- springmvc 孔浩 hibernate
以上为项目文件 用到的jar包:http://pan.baidu.com/s/1kT1Rsqj 1. model-User 2. beans.xml-去哪些包中找annotation:查找相应的实体类 ...
- 软件工程—WC功能实现 (JAVA)
软件工程-WC功能实现(JAVA) Github项目地址:https://github.com/Ousyoung/wc 项目要求 wc.exe 是一个常见的工具,它能统计文本文件的字符数.单词数和 ...
- 拷贝构造函数——防篡改
对于普通类型的对象来说,他们之间的复制是简单的,比如: int a = 88; int b = a; 而类和普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量. #include <io ...