Wagtail API v3 实战指南:基于 Django Ninja 的读写内容 API(Wagtail 8.0 预览版)
2026/9/14 11:05:16 网站建设 项目流程

Wagtail API v3 实战指南:基于 Django Ninja 的读写内容 API(Wagtail 8.0 预览版)

【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail

本文基于 Wagtail 8.0 引入的 v3 API 预览文档,讲解这套基于 Django Ninja 与类型提示构建的新 API:如何在 Django 项目中挂载它、如何通过 OpenAPI 3.1 Schema 做类型发现、如何使用 limit/offset 分页,以及如何理解其 RFC 7807 风格的结构化错误响应。读完本文,你可以将 v3 API 作为 Wagtail 内容后端的读写接口接入前端、Headless 站点或自动化脚本,并对照仓库源码理解每个行为的底层实现。

什么是 Wagtail API v3

Wagtail 8.0 引入了新版 v3 API 的预览(preview)。与传统 v2 REST API 不同,v3 的核心技术特征是:

  • 构建于 Django Ninja 之上:采用 Ninja 的 Router / Schema / 分页体系,而不是 v2 中手写的 view 与序列化器组合;
  • 全面使用类型提示(type hints):请求与响应结构以 Pydantic Schema 声明,从而支持类型检查与自动文档生成;
  • 导出 OpenAPI 3.1 Schema:客户端可以从openapi.json获得完整的机器可读接口定义;
  • 声明式的按类型 Schema:每种内容类型(如各类 Page 模型)都有独立的 read/create/patch Schema,而不是所有模型共用一个宽泛的序列化器;
  • 同时支持读取与写入的 CMS 操作:设计目标是覆盖 RFC 115 中描述的读写能力,大致对齐 Wagtail 后台管理界面所能完成的常见操作。

需要注意,v3 目前处于preview状态,官方建议挂载在/api/v3-preview/路径下,以表明其可能随后续版本调整。

快速开始:安装与挂载

1. 注册应用

首先把wagtail.api.v3加入 Django 项目的INSTALLED_APPS

# settings.py INSTALLED_APPS = [ ... 'wagtail.api.v3', ... ]

对应的应用配置类定义在 apps.py 中,应用标签为wagtailapi_v3;其ready()钩子会额外调用APIRichText.check_setting()校验富文本格式相关设置(详见下文“与 v2 共享的配置”)。

2. 挂载 URL

在项目的 URL 配置中注册 API。预览阶段推荐挂载到/api/v3-preview/

# urls.py from wagtail.api.v3.urls import api urlpatterns = [ path("api/v3-preview/", api.urls), # You can also mount it at /api/v3/ if you prefer: # path("api/v3/", api.urls), ]

从 urls.py 的模块文档字符串可以看到两点关键约束:

  1. 挂载方式与 v2 保持一致(api.urls是一个可挂载的 URL 对象),保证 v2 用户的迁移成本低;
  2. 所有 Router 必须在访问api.urls之前注册到api实例上,否则 Django Ninja 会抛出ConfigError。Wagtail 内部已经在 api.py 中完成了 Router 注册:
# wagtail/api/v3/api.py(节选) api.add_router("/pages/", pages_router) api.add_router("/schema/", schema_router) api.add_router("/sites/", sites_router) api.add_router("/", whoami_router)

也就是说,当前随包提供的路由包括页面(/pages/)、Schema 发现(/schema/)、站点(/sites/)以及根路径下的whoami端点。

3. 浏览生成的文档

挂载成功后,两个开箱即用的入口会自动暴露:

  • 人类可读的文档仪表盘<API root>/docs/(例如/api/v3-preview/docs/);
  • 机器可读的 OpenAPI Schema<API root>/openapi.json

这两个路由的定义可以直接在NinjaAPI实例参数中确认(api.py):

api = NinjaAPI( title="Wagtail API", version="3.0.0", description="Wagtail v3 read and write API", urls_namespace="wagtailapi_v3", docs_decorator=_gate_docs, openapi_url="/openapi.json", docs_url="/docs/", )

OpenAPI Schema 与文档仪表盘默认是公开可访问的(包含匿名端点与认证端点的描述)。如果希望隐藏这两条路由,把设置WAGTAILAPI_DOCS_ENABLED设为False即可。其实现是一个简单的装饰器门控(api.py):

def _gate_docs(view): """Uses ``WAGTAILAPI_DOCS_ENABLED`` to 404 the OpenAPI schema / interactive docs.""" @wraps(view) def wrapper(request, *args, **kwargs): if not getattr(settings, "WAGTAILAPI_DOCS_ENABLED", True): raise Http404 return view(request, *args, **kwargs) return wrapper

即:关闭后这两条路由会直接返回 404(而非 403),对探测者而言表现为“不存在”。

端点组成与按应用动态启用的机制

v3 API 暴露哪些端点,取决于项目中安装了哪些 Wagtail 应用:

  • Pages 端点始终可用
  • Images、Documents、Snippets、Locales、Redirects 端点只有在对应应用位于INSTALLED_APPS且相应模型完成注册时才会出现。

从源码结构看,这一动态发现机制由 registry.py 中的ContentTypeRegistry支撑:模块导入时会立即调用registry.register_defaults()注册所有页面模型,并为每个模型生成三个方向的 Pydantic Schema(read/create/patch):

# wagtail/api/v3/registry.py(节选) def register_defaults(self) -> None: """Register the content types shipped with Wagtail (currently ``pages``).""" from wagtail.models import get_page_models for model in get_page_models(): read_schema = read_generator.generate_schema(model, base_class=PageSchema) ... self.register(ContentTypeRegistration( name=model._meta.label, label=str(model._meta.verbose_name), read_schema=read_schema, create_schema=create_schema, patch_schema=patch_schema, ))

/schema/路由消费这个注册表,让客户端先“发现”有哪些内容类型,再查询每种类型的 read/create/patch JSON Schema;某个方向没有 Schema 时,会返回占位符{"description": "Not available."}。注册时机上的一个细节值得注意:源码注释说明register_defaults()之所以放在模块导入时而不是AppConfig.ready()中执行,是因为 Router 模块在自身导入阶段就要读取注册内容来构建请求/响应 Schema,若依赖ready()会引入INSTALLED_APPS顺序的隐式依赖(见 registry.py)。

v3 覆盖的功能范围

文档明确列出了 v3 API 支持的 CMS 操作,大致对齐 Wagtail 后台管理界面:

  • 页面(Pages),包括草稿(drafts)、修订(revisions)与页面操作(page actions);
  • 站点(Sites)、语言(locales)与重定向(redirects);
  • 图片(images)与文档(documents);
  • 启用了 API 的 Snippets;
  • 富文本,支持 HTML 与 Markdown 两种格式;
  • StreamField 内容;
  • Schema 发现与 OpenAPI 参考。

文档同时坦诚列出了当前尚未覆盖、期待社区反馈的方向:

  • 工作流 / 审核操作(提交、批准、驳回等);
  • Site settings 支持;
  • 更精确的 StreamField block Schema;
  • 复用该 API 的官方客户端库或 UI;
  • v2 API 的弃用与最终移除计划;
  • 官方 API 教程。

v3 的每个子主题在仓库文档中都有独立章节,可按需深入:

  • 认证
  • Pages 端点
  • Images 端点
  • Documents 端点
  • Snippets 端点
  • Redirects 端点
  • StreamField 内容
  • 富文本(HTML/Markdown)
  • Sites 端点
  • Locales 端点
  • Schema 发现
  • 完整参考
  • 从 v2 迁移指南

分页:limit/offset 与 count 语义

所有列表端点使用limit/offset 分页,响应中的count不受分页影响的总结果数

{ "count": 42, "items": [] }

通过?limit?offset查询参数翻页,WAGTAILAPI_LIMIT_MAX限制limit的上限。

实现细节见 pagination.py:WagtailLimitOffsetPagination继承 Ninja 的LimitOffsetPagination,并做了两处与 v2 对齐的关键调整:

  1. 默认limit为 20(Wagtail 的 API 默认值),而不是 Ninja 原生的 100:

    class Input(LimitOffsetPagination.Input): limit: int = Field(default=20, ge=1) offset: int = Field(default=0, ge=0)
  2. 超限行为不同:当limit超过WAGTAILAPI_LIMIT_MAX时,v3 直接抛出400错误("limit cannot be higher than {max}"),与 v2 行为一致;而 Ninja 基础分页器会静默地把 limit 截断到上限:

    def paginate_queryset(self, queryset, pagination, request, **params): max_limit = _get_max_limit() if max_limit != inf and pagination.limit > int(max_limit): raise HttpError(400, f"limit cannot be higher than {int(max_limit)}") return super().paginate_queryset(queryset, pagination, request, **params)

    其中WAGTAILAPI_LIMIT_MAX的默认值为 20,设为None时上限为无穷(见_get_max_limit)。

错误处理:RFC 7807 的 application/problem+json

v3 API 的所有受控错误统一使用 RFC 7807 定义的application/problem+json媒体类型返回。覆盖范围包括:

场景状态码说明
Schema / 内容 / 模型层校验失败422含逐字段errors列表
未认证访问受保护端点401Authentication required
已认证但权限不足403附带权限错误信息
资源不存在404Not found
富文本格式错误400RichTextFormatError触发

一个校验失败的响应示例:

{ "type": "about:blank", "title": "Unprocessable Entity", "status": 422, "detail": "Validation failed", "errors": [] }

对应的实现集中在 errors.py,几个值得了解的点:

  • 错误体结构由 Pydantic Schema 声明ProblemDetail包含typetitlestatusdetailerrors五个字段,默认typeabout:blanktitle缺省时取 HTTP 状态短语(如 422 对应"Unprocessable Entity");

  • 多种校验异常归一到 422:Pydantic 校验错误、Ninja 校验错误、DjangoValidationError以及表单校验异常(FormValidationError)都会经validation_error_handler统一包装为 422 响应,并各自转换出对应格式的errors列表;

  • 401 与 403 的判定逻辑:从源码注释看,v3不信任基于会话的认证——只有当 bearer token 成功解析出用户时才返回 403,否则一律 401(errors.py):

    @api.exception_handler(PermissionDenied) def permission_denied_handler(request, exc): # v3 never trusts session auth: 401 unless a bearer token resolved. if not request.user.is_authenticated: return problem_response(status=401, detail="Authentication required") return problem_response(status=403, detail=str(exc) or "Permission denied")
  • 未处理异常的边界行为:未匹配到任何处理器的异常不会被转换成 problem+json 信封。在生产环境(DEBUG=False)中它们会被重新抛出,交由 Django 自身的错误处理机制,因此响应格式可能不是application/problem+json;仅在DEBUG=True时才会以 500 的 problem 响应返回(errors.py)。

这些行为的回归测试分别位于 test_errors.py、test_docs.py 与 test_openapi_snapshot.py(后者用快照文件 openapi.json 锁定 OpenAPI 输出,可用于观察 Schema 的演进)。

与 v2 共享的配置项

v3 API 在适用之处读取与 v2 相同的WAGTAILAPI_*设置,这意味着已有 v2 项目的配置可以平滑沿用:

  • WAGTAILAPI_BASE_URL:用于生成响应中绝对 URL 的基础地址;
  • WAGTAILAPI_LIMIT_MAX:分页limit的上限(默认 20,None表示不限制);
  • WAGTAILAPI_SEARCH_ENABLED:是否启用搜索参数;
  • WAGTAILAPI_RICH_TEXT_FORMAT:富文本输出格式(HTML / Markdown)。

各设置的完整定义见 API 设置参考,v2 的配置说明见 v2 配置文档。

集成建议与小结

  • 由于 v3 处于 preview 阶段,接入时请挂载在/api/v3-preview/并关注 迁移指南 中关于 v2 → v3 差异的说明;
  • 优先利用openapi.json/docs/做客户端代码生成,而非手工拼请求;需要对外隐藏文档时设置WAGTAILAPI_DOCS_ENABLED = False
  • 客户端错误处理应以application/problem+json为契约:先检查statustitle,再解析 422 响应中的errors列表做字段级提示;
  • 记住分页语义:count是总数,limit默认 20,超过WAGTAILAPI_LIMIT_MAX会得到 400 而不是被静默截断;
  • 需要写操作时,先通过 认证文档 配置 token 认证,v3 对会话认证的信任策略与 v2 不同(未解析出 bearer token 一律 401)。

整体来看,v3 API 用 Django Ninja + Pydantic 类型体系换取了自动化的 OpenAPI 3.1 文档、按内容类型声明的 read/create/patch Schema,以及结构化的 RFC 7807 错误契约——这正是它对 v2 的核心增量,也是其作为 Wagtail Headless 读写接口未来演进方向的基础。

【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询