2

有没有比这更好的方法来制作ListBlock具有固定内容的子类?

class MixedMediaCarouselBlock(blocks.ListBlock):
    """
    A hghly streamlined CarouselBlock which displays only Images and/or Videos.
    """

    def __init__(self, child_block=None, **kwargs):
        child_block = blocks.StructBlock([
            ('image', ImageChooserBlock(required=False)),
            ('video', EmbedBlock(
                label="Video URL",
                help_text="Paste the video URL from YouTube or Vimeo."
                          " e.g. https://www.youtube.com/watch?v=l3Pz_xQZVDg"
                          " or https://vimeo.com/207076450.",
                required=False
                )
            ),
        ])
        super(MixedMediaCarouselBlock, self).__init__(child_block, **kwargs)

    class Meta:
        template = 'core/blocks/mixed_media_carousel_block.html'
        label = 'Mixed Media Carousel'
        icon = 'media'

这样做感觉真的很糟糕,但我找不到任何其他明显的方法,因为它的构造函数ListBlock需要该child_block参数,而其他块类型则不需要。

我想要一个ListBlock子类的原因是将它用作我的一个页面中的单个块类型StreamField

class News(Page)
    assets = StreamField(
        MixedMediaCarouselBlock(), 
        help_text='Pick one or more images/videos to place in the sidebar of this article.'
    )

也许我只是做错了?

编辑:是的,我肯定做错了什么。这根本不起作用,因为当我尝试使用此设置保存页面并进入AttributeError: 'MixedMediaCarouselBlock' object has no attribute 'child_blocks'wagtail/wagtailcore/blocks/stream_block.py 第 401 行时才发现。(Wagtail 1.12.2)。不过不知道为什么。

EDIT2:我听从@gasman 的建议,想出了这个:

class MixedMediaCarouselBlock(blocks.StreamBlock):
    slides = blocks.ListBlock(
        blocks.StructBlock([
            ('image', ImageChooserBlock(required=False)),
            ('video', EmbedBlock(
                label="Video URL",
                help_text="Paste the video URL from YouTube or Vimeo."
                          " e.g. https://www.youtube.com/watch?v=l3Pz_xQZVDg or https://vimeo.com/207076450.",
                required=False
                )
            ),
        ])
    )

但我仍然在页面编辑表单上得到一个块类型菜单(我正在使用 wagtail-facelift):

块类型菜单

更糟糕的是,表单允许我slides向 StreamField 添加多个实例,这很容易导致用户不小心制作了多个单元素 ListBlock,而不是一个多元素 ListBlock,这会破坏渲染器。我能做些什么呢?

EDIT3:这是我经过大量实验后想出的,但我一点也不喜欢它。

class MixedMediaCarouselBlock(blocks.StructBlock):
    """
    A hghly streamlined CarouselBlock which displays only Images and/or Videos.
    """

    slides = blocks.ListBlock(
        blocks.StructBlock([
            ('image', ImageChooserBlock(required=False)),
            ('video', EmbedBlock(
                label="Video URL",
                help_text="Paste the video URL from YouTube or Vimeo."
                          " e.g. https://www.youtube.com/watch?v=l3Pz_xQZVDg or https://vimeo.com/207076450.",
                required=False
                )
            ),
        ])
    )

    class Meta:
        template = 'core/blocks/mixed_media_carousel_block.html'
        label = 'Mixed Media Carousel'
        icon = 'media'

class News(Page):
    ...
    assets = StreamField(
        ('media', MixedMediaCarouselBlock()),
        help_text='Pick one or more images/videos to place in the sidebar of this article.'
    )

然后,为了解决用户在单个 s 中意外添加多个MixedMediaCarouselBlocks 而不是多个视频/图像的问题MixedMediaCarouselBlock,我修改了一些 LESS CSS 来隐藏允许他们这样做的 UI:

body.model-news {
  .stream-menu .toggle {
    display: none;
  }

  .sequence-controls:not(.list-controls) button[id$=delete] {
    display: none;
  }
}

在我的研发过程中,我遇到了 Wagtail 1.12 补丁说明,其中提到将min_nummax_numblock_counts元属性添加到StreamBlock,这听起来很有希望。但它们不会影响 UI;他们只是添加了服务器端验证,使无效的选择在 POST 后显示为表单错误。如果他们一开始就阻止了无效的更改,我实际上可以使用它们。

4

1 回答 1

4

据我所知,您的块定义是正确的。ListBlock 不是为子类而设计的,因此尝试这样做不可避免地会有点麻烦,并且不能保证在 Wagtail 版本中保持稳定 - 但您不依赖 ListBlock 的任何内部结构,只更改构造函数的方法签名,所以它应该足够安全。请记住,如果您对除 StructBlock、StreamBlock 或 ChoiceBlock 之外的任何块进行子类化,对子类的引用将出现在迁移文件中,因此只要这些迁移存在,您就有责任保持类定义到位:请参阅http: //docs.wagtail.io/en/v1.12.2/topics/streamfield.html#streamfield-definitions-within-migrations

这里的问题是 StreamBlock(和子类)是当前唯一允许作为 StreamField 的顶级块的块类型:允许其他块类型已被提议(#2048)但尚未实现。作为一种解决方法,您可以将 MixedMediaCarouselBlock 定义为只有一个子块类型的 StreamBlock;这并不像听起来那么笨拙,因为在这种情况下,选择块类型的菜单被跳过(#1696),这使得行为或多或少与 ListBlock 相同。

于 2017-10-13T10:00:24.233 回答