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

Wagtail API按author_name与author_status过滤失败求助

如何在Wagtail API中按嵌套的author_name和author_status过滤数据

我用Wagtail搭建了API接口127.0.0.1:8000/api/v2/stories,返回结果包含blog_authors嵌套数据,里面有author_name和author_status字段。尝试用?author_name=Test、?blog_authors__author_name=Test、?author__name=Test这些URL参数过滤时,都返回错误:"query parameter is not an operation or a recognised field: author_name"。我已经把相关字段加入known_query_parameters,也试过DjangoFilterBackend,但问题还是没解决,求问怎么实现按author_name和author_status过滤API数据。


api.py

class ProdPagesAPIViewSet(BaseAPIViewSet):
    renderer_classes = [JSONRenderer]
    pagination_class = CustomPagination
    filter_backends = [FieldsFilter,
        ChildOfFilter,
        AncestorOfFilter,
        DescendantOfFilter,
        OrderingFilter,
        TranslationOfFilter,
        LocaleFilter,
        SearchFilter,]
    known_query_parameters = frozenset(
        [
            "limit",
            "offset",
            "fields",
            "order",
            "search",
            "search_operator",
            # Used by jQuery for cache-busting. See #1671
            "_",
            # Required by BrowsableAPIRenderer
            "format",
            "page","author_name",
        ]
    )
    meta_fields = ["type","seo_title","search_description","first_published_at"]
    body_fields = ["id","type","seo_title","search_description","first_published_at","title"]
    listing_default_fields = ["type","seo_title","search_description","first_published_at","id","title","alternative_title","news_slug","blog_image","video_thumbnail","categories","blog_authors","excerpt","content","content2","tags","story_type"]
    nested_default_fields = []
    def get_queryset(self):
        return super().get_queryset().filter(story_type='Story').order_by('-first_published_at')
    name = "stories"
    model = AddStory

api_router.register_endpoint("stories", ProdPagesAPIViewSet)

models.py(blog_authors相关)

class BlogAuthorsOrderable(Orderable):
    """This allows us to select one or more blog authors from Snippets."""

    page = ParentalKey("blog.AddStory", related_name="blog_authors")
    author = models.ForeignKey(
        "blog.BlogAuthor",
        on_delete=models.CASCADE,
    )

    panels = [
        # Use a SnippetChooserPanel because blog.BlogAuthor is registered as a snippet
        FieldPanel("author"),
    ]

    @property
    def author_name(self):
        return self.author.name

    @property
    def author_website(self):
        return self.author.website

    @property
    def author_image(self):
        return self.author.image
    
    @property
    def author_status(self):
        return self.author.status

    api_fields = [
        APIField("author_name"),
        APIField("author_website"),
        APIField("author_status"),
        # This is using a custom django rest framework serializer
        APIField("author_image", serializer=ImageSerializedField()),
        # The below APIField is using a Wagtail-built DRF Serializer that supports
        # custom image rendition sizes
        APIField(
            "image",
            serializer=ImageRenditionField("fill-200x250|format-webp",
                source="author_image"
            )
        ),
    ]


@register_snippet
class BlogAuthor(models.Model):
    """Blog author for snippets."""


    name = models.CharField(max_length=100)
    status = models.BooleanField(default=False, verbose_name="Special Author")
    website = models.URLField(blank=True, null=True)
    image = models.ForeignKey(
        "wagtailimages.Image",
        on_delete=models.SET_NULL,
        null=True,
        blank=False,
        related_name="+",
    )

    panels = [
        MultiFieldPanel(
            [
                FieldPanel("name"),
                # Use an ImageChooserPanel because wagtailimages.Image (image property)
                # is a ForeignKey to an Image
                FieldPanel("image"),
                FieldPanel("status"),
            ],
            heading="Name, Image and Status",
        ),
        MultiFieldPanel(
            [
                FieldPanel("website"),
            ],
            heading="Links"
        )
    ]

    def __str__(self):
        """String repr of this class."""
        return self.name

    class Meta:  # noqa
        verbose_name = "Blog Author"
        verbose_name_plural = "Blog Authors"

AddStory模型中调用blog_authors

class AddStory(Page):
    """Blog detail page."""
content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                InlinePanel("blog_authors", label="Author", min_num=1, max_num=4)
            ],
            heading="Author(s)"
        ),
    ]
api_fields = [
        APIField("blog_authors"),
]

解决方案

1. 自定义过滤后端处理嵌套字段查询

Wagtail默认的FieldsFilter不支持跨关联模型的嵌套过滤,需要自定义过滤后端来解析这类参数:

from wagtail.api.v2.filters import FieldsFilter

class NestedFieldsFilter(FieldsFilter):
    def filter_queryset(self, request, queryset, view):
        for param_name, value in request.query_params.items():
            # 处理blog_authors__author_name和blog_authors__author_status参数
            if param_name.startswith('blog_authors__author_'):
                # 转换为Django ORM可识别的关联查询路径
                orm_param = param_name.replace('blog_authors__author_', 'blog_authors__author__')
                queryset = queryset.filter(**{orm_param: value})
        return super().filter_queryset(request, queryset, view)

2. 更新API视图集配置

  • 替换原有的FieldsFilter为自定义的NestedFieldsFilter
  • 在known_query_parameters中添加允许的过滤参数

修改后的api.py关键部分:

# 导入自定义过滤后端(根据实际路径调整)
from .filters import NestedFieldsFilter

class ProdPagesAPIViewSet(BaseAPIViewSet):
    renderer_classes = [JSONRenderer]
    pagination_class = CustomPagination
    filter_backends = [NestedFieldsFilter,
        ChildOfFilter,
        AncestorOfFilter,
        DescendantOfFilter,
        OrderingFilter,
        TranslationOfFilter,
        LocaleFilter,
        SearchFilter,]
    known_query_parameters = frozenset(
        [
            "limit",
            "offset",
            "fields",
            "order",
            "search",
            "search_operator",
            "_",
            "format",
            "page",
            "blog_authors__author_name",
            "blog_authors__author_status",
        ]
    )
    # 其余代码保持不变

3. 正确调用API进行过滤

现在可以使用以下参数实现过滤:

  • 按作者名称过滤:127.0.0.1:8000/api/v2/stories?blog_authors__author_name=Test
  • 按作者特殊状态过滤:127.0.0.1:8000/api/v2/stories?blog_authors__author_status=True

核心原理

自定义过滤后端将API传入的blog_authors__author_name参数转换为Django ORM能识别的blog_authors__author__name——因为BlogAuthorsOrderable模型中的author是关联BlogAuthor的外键,name是BlogAuthor的原生字段,这样ORM就能正确关联查询。同时必须将这些参数加入known_query_parameters,否则Wagtail会判定为非法参数返回错误。


内容的提问来源于stack exchange,提问作者Mohammad Fathi Rahman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 12:40:38