如何定义具有固定内容的ListBlock的子类?

时间:2017-10-13 01:00:18

标签: django wagtail wagtail-streamfield

有没有比这更好的方法来创建具有固定内容的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.'
    )

也许我只是做错了?

编辑:是的,我确实做错了什么。这根本不起作用,正如我刚刚发现的那样,当我尝试使用此设置保存页面并在wagtail / wagtailcore / blocks / stream_block.py第401行上获得AttributeError: 'MixedMediaCarouselBlock' object has no attribute 'child_blocks'时。(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
                )
            ),
        ])
    )

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

Block Type Menu

更糟糕的是,该表单允许我向StreamField添加多个slides实例,这很容易导致用户意外地生成多个单元素ListBlocks,而不是一个多元素ListBlock,这会破坏渲染器。我该怎么办?

编辑3:这是我经过多次实验后想出来的,但我根本不喜欢它。

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.'
    )

然后,为了解决用户在一个MixedMediaCarouselBlock内意外添加多个MixedMediaCarouselBlock而不是多个视频/图片的问题,我破解了一些LESS CSS来隐藏可以让他们做的UI的是:

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

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

在我的R& D期间,我遇到了Wagtail 1.12补丁说明,其中提到min_nummax_numblock_counts meta attrs添加到StreamBlock ,听起来很有希望。但它们不会影响用户界面;他们只是添加了服务器端验证,使得无效选择在POST后显示为表单错误。如果他们首先阻止了无效的更改,我实际上可以使用它们。

1 个答案:

答案 0 :(得分:2)

据我所知,您的块定义是正确的。 ListBlock不是设计为子类的,所以尝试这样做不可避免地会有点hacky并且不能保证在Wagtail发行版中保持稳定 - 但是你不依赖于ListBlock的任何内部,只是改变构造函数的方法签名,所以它应该足够安全。请记住,如果您继承StructBlock,StreamBlock或ChoiceBlock以外的任何块,则对您的子类的引用将出现在迁移文件中,因此,只要存在这些迁移,您就有责任保持类定义:请参阅{{ 3}}

这里的问题是StreamBlock(和子类)是目前唯一允许作为StreamField的顶级块的块类型:允许其他块类型(http://docs.wagtail.io/en/v1.12.2/topics/streamfield.html#streamfield-definitions-within-migrations)但尚未实现然而。作为解决方法,您可以将MixedMediaCarouselBlock定义为仅具有一个子块类型的StreamBlock;这并不像听起来那么笨重,因为在这种情况下会跳过选择块类型的菜单(#2048),这会使行为与ListBlock大致相同。