如何定义固定内容的ListBlock的子类?
How to define a subclass of ListBlock with fixed contents?
有没有比这更好的方法来创建具有固定内容的 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
)
),
])
)
但是我在页面编辑表单上仍然得到一个块类型的菜单(我正在使用 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.'
)
然后,为了处理用户不小心在单个 MixedMediaCarouselBlock
中添加多个 MixedMediaCarouselBlock
而不是多个 videos/images 的问题,我破解了一些 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_num
、max_num
和 block_counts
元属性添加到 StreamBlock
,听起来很有前途。但它们不会影响 UI;他们只是添加服务器端验证,使无效选择在 POST 后显示为表单错误。如果他们首先阻止了无效更改,我实际上可以使用它们。
据我所知,您的块定义是正确的。 ListBlock 并非设计为 subclassed,因此尝试这样做不可避免地会有点 hacky 并且不能保证在 Wagtail 版本中保持稳定 - 但您不依赖于任何内部结构ListBlock,只是改变了构造函数的方法签名,所以它应该足够安全。请记住,如果您子 class 除了 StructBlock、StreamBlock 或 ChoiceBlock 之外的任何块,对您的子 class 的引用将出现在迁移文件中,因此您有责任保留 class只要这些迁移存在,定义就会存在:请参阅 http://docs.wagtail.io/en/v1.12.2/topics/streamfield.html#streamfield-definitions-within-migrations.
这里的问题是 StreamBlock(和 subclasses)是目前唯一允许作为 StreamField 的顶级块的块类型:已提议允许其他块类型(#2048) but hasn't been implemented yet. As a workaround, you could define MixedMediaCarouselBlock as a StreamBlock with only one child block type; this isn't as clunky as it might sound, because in this situation the menu for choosing a block type is skipped (#1696), 这使得行为或多或少与 ListBlock 相同。
有没有比这更好的方法来创建具有固定内容的 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
)
),
])
)
但是我在页面编辑表单上仍然得到一个块类型的菜单(我正在使用 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.'
)
然后,为了处理用户不小心在单个 MixedMediaCarouselBlock
中添加多个 MixedMediaCarouselBlock
而不是多个 videos/images 的问题,我破解了一些 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_num
、max_num
和 block_counts
元属性添加到 StreamBlock
,听起来很有前途。但它们不会影响 UI;他们只是添加服务器端验证,使无效选择在 POST 后显示为表单错误。如果他们首先阻止了无效更改,我实际上可以使用它们。
据我所知,您的块定义是正确的。 ListBlock 并非设计为 subclassed,因此尝试这样做不可避免地会有点 hacky 并且不能保证在 Wagtail 版本中保持稳定 - 但您不依赖于任何内部结构ListBlock,只是改变了构造函数的方法签名,所以它应该足够安全。请记住,如果您子 class 除了 StructBlock、StreamBlock 或 ChoiceBlock 之外的任何块,对您的子 class 的引用将出现在迁移文件中,因此您有责任保留 class只要这些迁移存在,定义就会存在:请参阅 http://docs.wagtail.io/en/v1.12.2/topics/streamfield.html#streamfield-definitions-within-migrations.
这里的问题是 StreamBlock(和 subclasses)是目前唯一允许作为 StreamField 的顶级块的块类型:已提议允许其他块类型(#2048) but hasn't been implemented yet. As a workaround, you could define MixedMediaCarouselBlock as a StreamBlock with only one child block type; this isn't as clunky as it might sound, because in this situation the menu for choosing a block type is skipped (#1696), 这使得行为或多或少与 ListBlock 相同。