使用带有动态选择的 Wagtail "ChoiceBlock" 而不是硬编码列表

Using a Wagtail "ChoiceBlock" with dynamic choices rather than a hardcoded list

我们有一个博客模型的设置,该模型与 BlogPageCategory 具有多对多关系,并且我们有一个 "recent blog posts" streamfield 块,可让您指定是否显示 X 个最新博客文章或 X 个最新博客的卡片来自特定类别的帖子。

因此,我们从以下代码开始:

from wagtail.core import blocks


class RecentBlogEntries(blocks.StructBlock):
    title = blocks.CharBlock(
        required=True,
    )

    category_filter = blocks.ChoiceBlock(
        label='Filter by Category',
        required=False,
        choices=[
            ('all', 'All'),
            ('First Category', 'First Category'),
            ('...',. '...'),
        ],
    )

    ...

但是对类别进行硬编码有点愚蠢,能够从 "what the list is, right now, based on the CMS data for BlogPageCategory" 中选择它们会方便得多。但是,以下代码(当然)变成了同样硬编码的迁移:

from wagtail.core import blocks
from ... import BlogPageCategory


class RecentBlogEntries(blocks.StructBlock):
    title = blocks.CharBlock(
        required=True,
    )

    choices = [ (cat.name, cat.name) for cat in BlogPageCategory.objects.all()]
    choices.sort()
    choices.insert(0, ('all', 'All'))

    category_filter = blocks.ChoiceBlock(
        label='Filter by Category',
        required=False,
        choices=choices,
    )

    ...

有什么方法可以使它成为动态值而不是 makemigrations 固定的列表?

ChoiceBlock 接受一个可调用函数作为 choices 参数:

def get_categories():
    return [(cat.name, cat.name) for cat in BlogPageCategory.objects.all()]


class RecentBlogEntries(blocks.StructBlock):
    title = blocks.CharBlock(
        required=True,
    )

    category_filter = blocks.ChoiceBlock(
        label='Filter by Category',
        required=False,
        choices=get_categories,
    )

需要在模块的顶层定义可调用对象,以便迁移可以引用它(即它不能是 class 上的方法),如果它得到随后移动或重命名,您需要相应地编辑迁移。