使用 StreamBlockField 测试 Wagtail StructBlock

Testing a Wagtail StructBlock with a StreamBlockField

我正在尝试 运行 对由普通块字段和 StreamBlock 组成的 StructBlock 进行一些单元测试。

我 运行 遇到的问题是我可以构建一个渲染块的测试,但我无法测试 StreamBlock 验证(即,我无法测试块的 clean())

堆栈

块定义

MyStructBlock

    class MyStructBlock(blocks.StructBlock):
        image = ImageChooserBlock()

        heading = blocks.CharBlock(required=True)
        description = blocks.RichTextBlock(features=["bold", "italic"])
        links = blocks.StreamBlock([("link", LinkBlock())], icon="link", min_num=1, max_num=6)

        class Meta:
            icon = "list-ul"
            template = "blocks/two_column_with_link_list_block.html"

LinkBlock

    class LinkBlock(blocks.StructBlock):
        link_text = blocks.CharBlock()
        internal_link = blocks.PageChooserBlock()
        internal_document = DocumentChooserBlock()
        external_link = blocks.URLBlock()

通过这种设置,我创建了这种 block_definition:

{
   'image': <Image: Thumbnail of subject>, 
   'heading': 'SomeText', 
   'description': 'Hello, Son', 
   'links':  
     [
       {'value': 
         {
          'link_text': 'Walk trip thought region.', 
          'internal_link': None, 
          'document_link': None, 
          'external_link': 'www.example.com'
         }
       },
       {'value': {...}},
       {'value': {...}},
      ]
}

那么这样的测试就可以了:

   def test_structure():
      my_struct_block = MyStructBlock()
      block_def = block_definition
      rendered_block_html = BeautifulSoup(my_struct_block.render(value=block_def)))
      assert <<THINGS ABOUT THE STRUCTURE>>

块呈现并且一切都很好,但是当我尝试 clean 块时,一切都开始横向移动。

   def test_validation():
       my_struct_block = MyStructBlock()
       block_def = block_definition
       rendered_block_html = BeautifulSoup(my_struct_block.render(my_struct_block.clean(block_def)))

会产生这样的结果:

self = <wagtail.core.blocks.field_block.RichTextBlock object at 0x7f874f430580>, value = 'Hello, Son'

    def value_for_form(self, value):
        # Rich text editors take the source-HTML string as input (and takes care
        # of expanding it for the purposes of the editor)
>       return value.source
E       AttributeError: 'str' object has no attribute 'source'

.direnv/python-3.9.6/lib/python3.9/site-packages/wagtail/core/blocks/field_block.py:602: AttributeError

描述性足够了——我知道它需要 Richtext 块定义——但为什么不同?

注意: 我尝试使用 RichText 定义 block_definition 作为期望 RichText 的字段的值,但失败了。如果我删除 RichText 字段,则清理会在我用来定义 LinkBlock.

dicts 上失败

问题

是否有一种规范的方法来设置这样一个可以处理复杂块的测试,以便块定义的通用源可以测试 render 以及 cleanrender?或者,这种类型的块是否在某种程度上需要一种集成方法,在这种方法中,我构建一个页面并将复杂的块添加为 stream_data,然后请求该页面并使用响应的呈现?

每个块类型都有一个对应的 'native' 数据类型用于它希望使用的数据 - 对于更简单的块,这种数据类型就是您所期望的(例如,CharBlock 的字符串,图像ImageChooserBlock 的实例),但对于一些更复杂的实例,定义了一个自定义类型:

  • 对于 RichTextBlock,原生类型是 wagtail.core.rich_text.RichText(其行为类似于字符串,但也有一个 source 属性,例如页面链接中的页面 ID 保持不变)
  • 对于 StreamBlock,原生类型是 wagtail.core.blocks.StreamValue(一种序列类型,其中每个项目都是具有 block_typevalue 属性的 StreamValue)。

如果您使用错误的类型(例如 RichTextBlock 的字符串或 StreamBlock 的字典列表),render 方法通常会很宽容,因为它实际上只是调用您自己的模板代码。 clean 方法会更挑剔,因为它是每个块特定的 运行 Python 逻辑。

不幸的是,用于每个块的正确类型并没有真正正式记录,其中一些构造起来非常繁琐(例如,需要将 StructValue 传递给相应 StructBlock 的引用)- 在测试代码之外,没有太多需要从头开始创建这些对象,因为数据通常来自某些外部源(例如表单提交或数据库),并且每个块将负责将其转换为其本机类型。

考虑到这一点,我建议您借助 to_python 方法构建数据,该方法将 JSON 表示形式转换为存储在数据库中的形式(仅由简单的 Python 数据类型(整数、字符串、列表、字典)转换为本机数据类型:

block_def = my_struct_block.to_python({
   'image': 123,  # the image ID
   'heading': 'SomeText', 
   'description': 'Hello, Son', 
   'links':  
     [
       {'type': 'link', 'value': 
         {
          'link_text': 'Walk trip thought region.', 
          'internal_link': None, 
          'document_link': None, 
          'external_link': 'www.example.com'
         }
       },
       {'type': 'link', 'value': {...}},
       {'type': 'link', 'value': {...}},
      ]
})

my_struct_block.clean(block_def) 有望成功。