Python 现代开发学习笔记

(二十)组合完整工厂、配置与资源

1. 让应用创建完成后再接受请求

现在把已经讲过的功能登记到同一工厂:

# 项目文件:shelf_project/src/shelfweb/__init__.py
import os
from datetime import timedelta
from pathlib import Path

from flask import Flask


def create_app(test_config=None, instance_path=None):
    location = Path(instance_path or os.getenv("SHELF_INSTANCE", "instance")).resolve()
    location.mkdir(parents=True, exist_ok=True)
    app = Flask(__name__, instance_path=str(location))
    app.config.from_mapping(
        DATABASE=str(location / "books.sqlite3"),
        SECRET_KEY=os.getenv("SHELF_SECRET_KEY"),
        SESSION_COOKIE_NAME="shelf_session",
        SESSION_COOKIE_HTTPONLY=True,
        SESSION_COOKIE_SAMESITE="Lax",
        SESSION_COOKIE_SECURE=False,
        SESSION_REFRESH_EACH_REQUEST=False,
        PERMANENT_SESSION_LIFETIME=timedelta(hours=1),
        MAX_CONTENT_LENGTH=16 * 1024,
    )
    key_file = location / "secret.key"
    if app.config["SECRET_KEY"] is None and key_file.is_file():
        app.config["SECRET_KEY"] = key_file.read_text(encoding="utf-8").strip()
    if test_config:
        app.config.update(test_config)
    if not app.config["SECRET_KEY"]:
        raise RuntimeError("请先运行 shelf-books init,或设置 SHELF_SECRET_KEY")

    from . import api, auth, books, db, errors, security

    db.init_app(app)
    app.register_blueprint(auth.bp)  # 先登记用户加载,再登记全局表单检查。
    app.register_blueprint(books.bp)
    app.register_blueprint(api.bp)
    security.init_app(app)
    errors.init_app(app)
    return app

instance_path 保存这台机器的数据库和签名密钥。SHELF_INSTANCE 可以更换它,默认使用当前工作目录下的 instance;解析为绝对路径后传给 Flask。模板、SQL 和 CSS 跟随安装包,用户数据放在实例目录。

配置先给出默认值,再读取本机密钥,最后用传入的测试配置覆盖。SECRET_KEY 也可以由 SHELF_SECRET_KEY 提供。没有准备密钥时给出明确的初始化提示,不会把示例中的固定演示密钥用到完整书架里。

配置 本项目中的实际行为
DATABASE 打开实例目录中的 SQLite 文件
SESSION_COOKIE_NAME Cookie 名为 shelf_session
SESSION_COOKIE_HTTPONLY 页面脚本不能直接读这个 Cookie
SESSION_COOKIE_SAMESITE 使用 Lax 的浏览器 Cookie 发送规则
SESSION_COOKIE_SECURE 本机 HTTP 演示设为 False;HTTPS 部署设为 True
PERMANENT_SESSION_LIFETIME Cookie 与服务器登录记录采用一小时长度
SESSION_REFRESH_EACH_REQUEST 不因每次读取自动延长 Cookie 有效期
MAX_CONTENT_LENGTH 过大的请求体得到 413

from . import ... 在工厂中取得功能模块,依次登记连接收尾、Blueprint、表单检查和错误处理。用户加载先登记,CSRF 检查后登记,视图执行时已有 g.user。工厂完成配置与登记,之后交给服务器处理请求。

配置默认值和密钥后覆盖测试设置,再登记数据库收尾及三个 Blueprint,用户加载先于表单检查,最后返回已经组合好的 WSGI 应用

2. 目录按变化职责分开

位置 负责的工作
__init__.py 工厂、配置和功能登记
db.py、schema.sql 当前连接、表结构与初始化
validation.py、service.py 字段规则、归属、查询和事务
auth.py、security.py 登录恢复、会话、装饰器与 CSRF
books.py、api.py 页面动作和只读 JSON 入口
templates/ 骨架、宏、账号表单、书籍页面和错误页面
static/style.css 页面显示样式
cli.py 本机初始化与启动命令

配套 CSS 文件 延续浅色背景、靛蓝操作和青绿色状态,窄屏时卡片排列为一列。pyproject.toml 的 package-data 将模板、SQL 和样式带入安装包,离开源代码目录仍然能够加载它们。

笔记正文与全部题解 · Ctrl / ⌘ K