(二十)组合完整工厂、配置与资源
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 appinstance_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。工厂完成配置与登记,之后交给服务器处理请求。
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 和样式带入安装包,离开源代码目录仍然能够加载它们。