Wagtail 如何用 Django Ninja 构建自定义 API 并展示 OpenAPI 文档?

发布时间:2026/9/14 10:18:35
Wagtail 如何用 Django Ninja 构建自定义 API 并展示 OpenAPI 文档? Wagtail 如何用 Django Ninja 构建自定义 API 并展示 OpenAPI 文档【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail如果你的 Wagtail 站点需要一套自己控制响应结构的只读内容接口比如给移动端或前端框架供数官方文档从 8.0 版起提供了一条基于 Django Ninja 的路径用 Python 类型提示和 Pydantic 定义接口与响应模型Ninja 自动生成 OpenAPI schema 和带“在线试用”功能的文档页面。完成本文后你会在项目里得到一个挂载在/api/的自定义 API并能在/api/docs直接浏览 OpenAPI 文档。以下内容整理自 Django Ninja 配置指南适用前提是一个已装好 Wagtail 的 Django 项目。注意它与 Wagtail 内置的 v2/v3 API 是两条独立路线内置 API 见 Wagtail API 总览 和 API v3 说明本文只覆盖自定义 API。准备安装 django-ninja先安装django-ninja包pip install django-ninja文档还建议可选把ninja加入INSTALLED_APPS这样在使用 OpenAPI 文档查看器时不需要从外部加载静态文件# settings.py INSTALLED_APPS [ ... ninja, ]创建 API 实例并注册路由在项目根目录已有的urls.py旁边新建api.py实例化 NinjaAPI 路由器# api.py from typing import Literal from django.http import HttpRequest from django.shortcuts import get_object_or_404 from ninja import Field, ModelSchema, NinjaAPI from wagtail.models import Page api NinjaAPI()然后在urls.py中注册路由让 Django 能把请求分发进 API。注意api这一行必须出现在默认的 Wagtail 页面路由之前否则请求会被页面路由先截走# urls.py from .api import api urlpatterns [ ... path(api/, api.urls), ... # 确保 api 行位于默认 Wagtail 页面路由之前 path(, include(wagtail_urls)), ]上面的...代表你项目里已有的其他路由保持不变即可。此时就可以做一次基础验证浏览器访问/api/docs应能看到 OpenAPI 文档页面还没有任何可用端点。如果这个页面打不开先检查path(api/, api.urls)是否已加入urlpatterns且位置在 Wagtail 路由之前。第一个端点页面列表在api.py中定义一个返回站点所有页面的接口。这里用 Ninja 的ModelSchema从 Django 模型生成 schema并用Field的alias把模型的get_url()方法映射成响应里的url字段# api.py class BasePageSchema(ModelSchema): url: str Field(None, aliasget_url) class Meta: model Page fields [ id, title, slug, ] api.get(/pages/, responselist[BasePageSchema]) def list_pages(request: HttpRequest): return Page.objects.live().public().exclude(id1)api.get装饰器声明了路由路径和响应格式exclude(id1)用于排除页面树的根节点。调用/api/pages/后每个页面返回id、title、slug和url四个字段。如果需要按父页面过滤可以加一个child_of查询参数。Ninja 会把list_pages函数里的每个参数当作 query 参数并依据类型提示完成取值解析、校验和 OpenAPI schema 生成api.get(/pages/, responselist[BasePageSchema]) def list_pages(request: HttpRequest, child_of: int None): if child_of: return get_object_or_404(Page, idchild_of).get_children().live().public() # 排除页面树根节点 return Page.objects.live().public().exclude(id1)单个页面详情与多种页面类型用 Ninja 的路径参数获取page_id并基于BasePageSchema为具体页面类型示例中的BlogPage新建 schemafrom blog.models import BlogPage class BlogPageSchema(BasePageSchema, ModelSchema): class Meta(BasePageSchema.Meta): model BlogPage fields [ intro, ] api.get(/pages/{page_id}/, responseBlogPageSchema) def get_page(request: HttpRequest, page_id: int): return get_object_or_404(Page, idpage_id).specificget_object_or_404(Page, idpage_id).specific会返回该页面具体类型的实例因此响应里除了通用Page字段还会带上BlogPage的intro。如果站点有多种页面类型都要从同一接口返回逐个写端点会很繁琐。Ninja 支持用类型联合union语法把多个 schema 组合在一起同一个端点即可返回不同类型from home.models import HomePage class HomePageSchema(BasePageSchema, ModelSchema): class Meta(BasePageSchema.Meta): model HomePage api.get(/pages/{page_id}/, responseBlogPageSchema | HomePageSchema) def get_page(request: HttpRequest, page_id: int): return get_object_or_404(Page, idpage_id).specific要让 Pydantic 判断当前页面该按哪个 schema 校验和序列化需要给各 schema 标注content_type并在BasePageSchema上加一个 resolver 计算字段返回页面类型名class BasePageSchema(ModelSchema): url: str Field(None, aliasget_url) content_type: str staticmethod def resolve_content_type(page: Page) - str: return page.specific_class._meta.model_name class Meta: model Page fields [ id, title, slug, ]文档给出的标注方式是BasePageSchema定义为content_type: str任何页面类型都可用这个基类HomePageSchema设为content_type: Literal[homepage]BlogPageSchema设为content_type: Literal[blogpage]。可选扩展嵌套数据、富文本与图片以下内容只在接口需要返回更复杂的字段时才加入。嵌套数据当页面关联了其他模型如博客作者文档示例用带ParentalManyToManyField的 snippet可以直接在 schema 里用 resolver 补上数据而不是另开端点class BlogPageSchema(BasePageSchema, ModelSchema): content_type: Literal[blogpage] authors: list[str] [] class Meta(BasePageSchema.Meta): model BlogPage fields [ intro, ] staticmethod def resolve_authors(page: BlogPage, context) - list[str]: return [author.name for author in page.authors.all()]如果模型上已有能直接取到值的方法也可以改用Field的 alias 写法例如authors: list[str] Field([], aliasget_author_names)。富文本Wagtail 富文本字段在数据库里是特定内部格式详见富文本内部格式说明。API 通常应返回“展示”形态——把页面、图片引用替换为 URL——文档用expand_db_html完成这一转换from wagtail.rich_text import expand_db_html class HomePageSchema(BasePageSchema, ModelSchema): content_type: Literal[homepage] body: str class Meta(BasePageSchema.Meta): model HomePage staticmethod def resolve_body(page: HomePage, context) - str: return expand_db_html(page.body)body字段类型是strresolver 负责把内部表示转成 HTML。图片用 resolver 加get_renditions()取格式化后的图片 rendition并自定义RenditionSchema定义其 API 形态。rendition 的属性如file.url需要用Field/alias 方式取出from wagtail.images.models import AbstractRendition class RenditionSchema(ModelSchema): url: str Field(None, aliasfile.url) alt: str Field(None, aliasalt) class Meta: model AbstractRendition fields [ width, height, ]在BlogPageSchema中声明main_image: list[RenditionSchema] []并添加 resolverstaticmethod def resolve_main_image(page: BlogPage) - list[AbstractRendition]: filters [ fill-800x600|format-webp, fill-800x600, ] if image : page.main_image(): return image.get_renditions(*filters).values() return []这样 JSON 里main_image是一个数组每项包含url、alt、width、height四个属性优先使用 webp rendition取不到时回退到普通 rendition。查看 OpenAPI 文档Django Ninja 会根据你定义的 operations 和 schema 自动生成 OpenAPI 文档无需额外配置。在上面的示例就位后访问/api/docs即可看到文档页面它带一个文档查看器可以直接在浏览器里试用各个端点。如果新增端点后文档里没有更新检查两点端点是否定义在api.py中使用的同一个api实例上以及/api/docs是否仍是项目里第一个命中的路由。限制与替代路线本文路线是你自己维护 schema 和端点灵活性最高但响应结构、认证等都要自己写。如果你需要的就是 Wagtail 现成的内容读取接口内置 v2 API基于 Django REST Framework只读和 8.0 引入的 v3 API同样基于 Django Ninja提供 OpenAPI 3.1 schema 输出、API root/openapi.json机器可读 schema 和API root/docs/文档面板可以直接使用配置见 API v3 Quick start。两种内置 API 与本文的自定义 API 可以同时挂载在不同 URL 前缀下路由注册方式参照各自文档。【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考