You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何自定义Wagtail的PagesAPIViewSet实现slug+locale参数查询

问题:通过slug+locale参数查询Wagtail API单条数据

默认Wagtail API v2通过ID获取数据,我需要修改为支持用slug+locale参数查询。目前创建了api/v2/pages/?slug=slug端点,但返回的是包含单个结果的列表格式,示例返回如下:

{
  "meta": {
    "total_count": 1
  },
  "items": [
    {
      "id": 34,
      "meta": {
        "type": "blog.BlogPage",
        "detail_url": "http://apimabas.demodev.click/api/v2/pages/34/",
        "html_url": "http://apimabas.demodev.click/en/blogs/test/",
        "slug": "test",
        "first_published_at": "2023-03-31T06:11:02.402366Z",
        "locale": "en"
      },
      "title": "Comparison"
    }
  ]
}

当前使用的序列化器和视图代码:

class CustomPageSerializer(PageSerializer):
    def get_fields(self):
        fields = super().get_fields()
        fields['detail_url'] = serializers.SerializerMethodField()
        return fields
    
    def get_detail_url(self, obj):
        return self.context['request'].build_absolute_uri(obj.slug + '/')


class CustomPageViewSet(PagesAPIViewSet):
    lookup_field = 'slug'
    model = Page
    base_serializer_class = CustomPageSerializer

    def get_object(self):
        # Retrieve the object using the custom slug field
        slug = self.kwargs[self.lookup_field]
        try:
            obj = Page.objects.filter(slug=slug).first()
        except Page.DoesNotExist:
            raise NotFound

        # Return the object
        return obj.specific
    

    def detail_view(self, request):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

    @classmethod
    def get_urlpatterns(cls):
        """
        This returns a list of URL patterns for the endpoint
        """
        return [
            path("", cls.as_view({"get": "listing_view"}), name="listing"),
            path("<slug:slug>/", cls.as_view({"get": "detail_view"}), name="detail"),
            path("find/", cls.as_view({"get": "find_view"}), name="find"),
        ]

解决方案

1. 查询参数式查询(推荐)

修改视图集,让find_view支持通过?slug=xxx&locale=xx查询,并返回单条数据而非列表:

from wagtail.core.models import Page, Locale
from rest_framework.exceptions import NotFound

class CustomPageSerializer(PageSerializer):
    def get_fields(self):
        fields = super().get_fields()
        fields['detail_url'] = serializers.SerializerMethodField()
        return fields
    
    def get_detail_url(self, obj):
        # 优化:生成带语言前缀的详情URL
        return self.context['request'].build_absolute_uri(f"/{obj.locale.language_code}/{obj.slug}/")


class CustomPageViewSet(PagesAPIViewSet):
    lookup_field = 'slug'
    model = Page
    base_serializer_class = CustomPageSerializer

    def get_object(self):
        # 从查询参数获取slug和locale
        slug = self.request.query_params.get('slug')
        locale_code = self.request.query_params.get('locale')
        
        if not slug or not locale_code:
            raise NotFound("必须提供slug和locale参数")
        
        try:
            # 验证locale有效性
            locale = Locale.objects.get(language_code=locale_code)
            # 同时过滤slug和locale
            obj = Page.objects.filter(slug=slug, locale=locale).first()
        except Locale.DoesNotExist:
            raise NotFound(f"不存在语言代码为{locale_code}的区域设置")
        
        if not obj:
            raise NotFound(f"未找到slug为{slug}且locale为{locale_code}的页面")
        
        return obj.specific

    def find_view(self, request):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

    @classmethod
    def get_urlpatterns(cls):
        return [
            path("", cls.as_view({"get": "listing_view"}), name="listing"),
            path("<slug:slug>/", cls.as_view({"get": "detail_view"}), name="detail"),
            path("find/", cls.as_view({"get": "find_view"}), name="find"),
        ]

使用方式:请求api/v2/pages/find/?slug=test&locale=en,返回单条数据格式:

{
  "id": 34,
  "meta": {
    "type": "blog.BlogPage",
    "detail_url": "http://apimabas.demodev.click/en/test/",
    "html_url": "http://apimabas.demodev.click/en/blogs/test/",
    "slug": "test",
    "first_published_at": "2023-03-31T06:11:02.402366Z",
    "locale": "en"
  },
  "title": "Comparison"
}

2. URL路径式查询

如果偏好通过URL路径传递参数(如api/v2/pages/test/en/),修改URL模式和get_object方法:

class CustomPageViewSet(PagesAPIViewSet):
    # ... 其他代码不变
    
    def get_object(self):
        # 从URL路径参数获取slug和locale
        slug = self.kwargs.get('slug')
        locale_code = self.kwargs.get('locale')
        
        if not slug or not locale_code:
            raise NotFound("必须提供slug和locale参数")
        
        try:
            locale = Locale.objects.get(language_code=locale_code)
            obj = Page.objects.filter(slug=slug, locale=locale).first()
        except Locale.DoesNotExist:
            raise NotFound(f"不存在语言代码为{locale_code}的区域设置")
        
        if not obj:
            raise NotFound(f"未找到slug为{slug}且locale为{locale_code}的页面")
        
        return obj.specific

    @classmethod
    def get_urlpatterns(cls):
        return [
            path("", cls.as_view({"get": "listing_view"}), name="listing"),
            # 修改detail路径,添加locale参数
            path("<slug:slug>/<str:locale>/", cls.as_view({"get": "detail_view"}), name="detail"),
            path("find/", cls.as_view({"get": "find_view"}), name="find"),
        ]

使用方式:请求api/v2/pages/test/en/即可返回对应单条数据。


内容的提问来源于stack exchange,提问作者Zekiyev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:52:30