使用 StreamBlock 时如何解决 Wagtail 循环块依赖

How to solve Wagtail cyclic Block dependency when using a StreamBlock

我想实现这样的目标,

from wagtail.wagtailcore.blocks import StreamBlock, StructBlock


class CarouselBlock(StructBlock):

    content = StreamBlock([
        ('tab', TabBlock()),
        ('carousel', CarouselBlock())
    ])


class TabBlock(StructBlock):

    content = StreamBlock([
        ('tab', TabBlock()),
        ('carousel', CarouselBlock())
    ])

在旋转木马中,我可以添加一个选项卡或另一个旋转木马,在一个选项卡内,我可以添加一个旋转木马或另一个选项卡。

处理此类编程案例的最佳做法是什么。

不幸的是,即使您找到了在定义中设置循环引用的方法,我也不认为它可以实现。 Wagtail 的代码中有很多地方会尝试将定义遍历为树,并以无限递归结束。

例如,当冻结迁移中的 StreamField 定义时会发生这种情况 - 它会将对命名 StructBlock / StreamBlock 子类的任何引用扩展到普通 StructBlock / StreamBlock 构造函数(请参阅 http://docs.wagtail.io/en/v1.5.2/topics/streamfield.html#streamfield-definitions-within-migrations),这将无限扩展这个案例。同样,为编辑表单构建 HTML 将失败,因为它将尝试为表单中的每个可重复元素构建一个 HTML 模板(即 HTML 的块将在任何时候添加你点击添加一个新的轮播,或一个新的标签) - 并且它不够聪明,无法为顶级轮播,二级轮播,三级轮播等重复使用相同的模板,所以会有生成无限数量的模板。

您需要对嵌套层数设置硬编码限制(例如,CarouselBlock 可以包含一个 SecondLevelCarousel 块,该块可以包含 ThirdLevelCarousel 块,但仅此而已),或者想出一种替代数据表示,将数据条目分布在多个视图中,而不是单一的无限嵌套形式。例如,您可以将 Carousel 和 Tab 定义为片段模型,并使用 SnippetChooserBlock 定义它们之间的 parent/child 个链接:

@register_snippet
class Carousel(models.Model):
    content = StreamField([
        ('carousel', blocks.SnippetChooserBlock('myapp.Carousel')),
        ('tab', blocks.SnippetChooserBlock('myapp.Tab')),
    ])

(当然,如果你走这条路,你必须确保不建立任何循环parent/child关系,否则你又回到原点:-))