如何自定义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
相关产品推荐
相关产品推荐

