Flask 进阶
本周我们会介绍一种大型 Flask 项目的文件架构. 尽管 Flask 并不强制要求项目的具体组织方式(换句话说任凭你放飞自我, 写在单文件里也是没问题的), 但是采用一种具体的组织方式总能使你的编码更清晰.
$ tree -P '*.py' --prune -I 'flask-sqlacodegen|venv'
.
├── app
│ ├── api
│ │ ├── __init__.py
│ │ └── posts.py
│ └── __init__.py
├── config.py
├── models.py
├── run.py
└── test.py配置选项
应用经常需要设定多个配置. 这方面最好的例子就是开发、测试和生产环境要使用不同的数据库, 这样才不会彼此影响.
我们可以设计一个名为配置的基类, 里面存放一些通用的配置选项, 例如密钥, 发送邮箱的名称, 等等. 然后在不同的具体配置中, 设置例如数据库地址, 是否使用调试器等.
为了让配置方式更灵活且更安全, 多数配置都可以从环境变量中导入.
你当然可以直接写成字面量:
MY_PASSWORD = 'hard to guess haha'然后你忘记改就把文件上传到 Git 里了, 然后被迫用到了我们学到的永久删除大法.
写成
MY_PASSWORD = os.environ.get('MY_PASSWORD')或者更好, 对部分字段做一个空值检查或者添加一个默认值
or 'default'
你可以使用 dotenv, 它可以将 .env 文件里的键值对当成系统变量来用. 具体使用方式可以参考文档.
千万不要把密码或其他机密信息写在纳入版本控制的配置文件中! .gitignore 有大用.
为了再给应用提供一种定制配置的方式, Config 类及其子类可以定义 init_app() 类的静态方法, 其参数为应用实例:
config 字典中注册了不同的配置环境. 怎么让应用加载不同的配置呢? 也就是 Config 类实例化以后怎样让 Flask 的应用实例加载这些配置.
要读取全部字段然后一个个设置
app.config?太麻烦了. 其实有更简洁的方式.
你可以用
app.config.from_object()来导入我们的配置类.
工厂函数
啥子是个工厂函数哟?
通俗地来说, 它就像工厂一样, 批量生产.
批量生产啥呢? 那就是应用实例.
换个人话: 把创建实例的过程用一个函数封装起来.
这样, debug 要测试不同环境的应用, 那就批量生产不同的应用实例就可以.
构造文件导入了大多数正在使用的 Flask 扩展(这里是 db), 你还可以添加例如 from flask_mail import Mail, 实例化一个 mail 然后 mail.init_app.
蓝图
上面的示例函数并不完整. 你马上就会出现一个问题:
既然我们的应用实例由
create_app创建. 那定义路由的那个 app 要写什么?总不至于
app = create_app(...)再写吧? 太晚了.
错误页面处理程序使用
app.errorhandler装饰器定义, 也是同样的问题...
Flask 使用蓝图 (blueprint) 提供了更好的解决方法. 蓝图和应用类似, 也可以定义路由和错误处理程序. 不同的是, 在蓝本中定义的路由和错误处理程序处于休眠状态, 直到蓝本注册到应用上之后, 它们才真正成为应用的一部分.
与应用一样, 蓝图可以在单个文件中定义, 也可使用更结构化的方式在包中的多个模块中创建. 为了方便调整, 我们在 api 文件夹下新建一个 __init__.py.
蓝本通过实例化一个 Blueprint 类对象创建. 这个构造函数有两个必须指定的参数: 蓝本的名称和蓝本所在的包或模块. 与应用一样, 多数情况下第二个参数使用Python 的 __name__ 变量即可. 此外, 在我们的示例中, url_prefix 参数表示这个蓝本路由的 url 会自带一个 '/api' 的前缀.
应用的路由保存在 authentication, posts, subvues, users 这几个模块中, 导入这两个模块就能把路由和错误处理程序(另外添加)与蓝本关联起来.
注意, 引入模块这一步不可以缺失, 也不可以随意调换位置, 否则可能会出现循环引用的情况出现.
**还有一件事**(老爹音), 引用模块的句法, from . 表示从当前目录下引用, 这是一种相对引用的方式, 这样的写法要比绝对引用好. 另外, from .. 表示从上一目录引用.
在 users.py 文件里, 就可以这么写了:
记得在创建应用的工厂函数里加上使用蓝图
然后应用脚本这么用
需求文件
你可能在别人的 Python 项目里见过 requirements.txt 这一个文件. pip 的 -r 选项读取一个文件中需求的库作为输入:
现在问题是, 怎么生成这个文件. 我们有两种方式.
把全部需求文件全部输出. 如数家珍.
只把要用的输出. 比较推荐这个.
测试
自动化测试是很重要的一环.
你也不想每次都打开浏览器输一遍 url 吧? 不想掏出 curl 敲一遍 HTTP 请求吧?
我们用 unitest 这个框架来编写测试代码: 假如你有一个计算器模块
一些常见的断言方法, 由名字也能知道它们是干啥的.
assertEqual(a, b)
assertNotEqual(a, b)
assertTrue(x)
assertFalse(x)
assertIs(a, b)
assertIsNot(a, b)
assertIsNone(x)
assertIsNotNone(x)
assertIn(a, b)
assertNotIn(a, b)
试试 coverage: 代码覆盖度工具用于统计单元测试检查了应用的多少功能.
一些工程上的实现
用户身份验证
Flask-Login:管理已登录用户的用户会话
Flask-HTTPAuth:HTTP 验证用户身份
Werkzeug:计算密码散列值并进行核对
itsdangerous:生成并核对加密安全令牌
详情见拓展阅读.
拓展阅读
Salted Password Hashing - Doing it Right: 你可能会想, 为啥要大费周章用一套加密的登录机制, 简单地通过某种 hash 映射一下不就完了, 甚至设计了一套自己的算法. 阅读这个文章可能会让你改变一些看法.
Flask Web开发: 基于Python的Web应用开发实战. 第2版: 这也是我们一开始推荐的 Flask 阅读图书. 你可以阅读第七章以及后面的章节(例如十四章讲的是RESTful接口)来进一步学习.
Hello Flask: 测试. 这个开源教程对测试进行了比较详细的介绍.
最后更新于
这有帮助吗?