feat: 实现预览模式(wagtail-headless-preview + Next.js Draft Mode)

This commit is contained in:
2026-08-07 15:34:46 +08:00
parent 16e4f97e74
commit 8d440b9e4a
5 changed files with 61 additions and 5 deletions
+42
View File
@@ -2,13 +2,55 @@
Wagtail API v2 路由配置。
详见 documents/设计方案分析与完善版.md §2.7 Headless API 设计规范。
"""
from django.contrib.contenttypes.models import ContentType
from rest_framework.response import Response
from wagtail.api.v2.views import PagesAPIViewSet
from wagtail.api.v2.router import WagtailAPIRouter
from wagtail.images.api.v2.views import ImagesAPIViewSet
from wagtail.documents.api.v2.views import DocumentsAPIViewSet
from wagtail_headless_preview.models import PagePreview
api_router = WagtailAPIRouter("wagtailapi")
api_router.register_endpoint("pages", PagesAPIViewSet)
api_router.register_endpoint("images", ImagesAPIViewSet)
api_router.register_endpoint("documents", DocumentsAPIViewSet)
class PagePreviewAPIViewSet(PagesAPIViewSet):
"""
预览模式专用只读接口:根据 `content_type`app_label.model,小写)+ `token`
从 `wagtail_headless_preview` 存储的草稿快照中取出对应页面实例并序列化返回。
`token` 由编辑器在 Wagtail 后台点击"预览"时生成(详见 §2.15/§2.7),
未知或已过期的 token 会返回 404,不会暴露任何非预览用途的草稿数据。
"""
known_query_parameters = PagesAPIViewSet.known_query_parameters.union(
["content_type", "token"]
)
def listing_view(self, request):
# 统一走 detail_view,保证序列化格式与详情接口一致;pk 仅占位,实际查找依赖 content_type+token。
self.action = "detail_view"
return self.detail_view(request, 0)
def detail_view(self, request, pk):
page = self.get_object()
serializer = self.get_serializer(page)
return Response(serializer.data)
def get_object(self):
app_label, model = self.request.GET["content_type"].split(".")
content_type = ContentType.objects.get(app_label=app_label, model=model)
page_preview = PagePreview.objects.get(
content_type=content_type, token=self.request.GET["token"]
)
page = page_preview.as_page()
if not page.pk:
# 新建(尚未保存)页面的预览没有真实主键,填充占位值避免 API 路由生成 URL 时报错
page.pk = 0
return page
api_router.register_endpoint("page_preview", PagePreviewAPIViewSet)