使用 StreamBlockField 测试 Wagtail StructBlock
Testing a Wagtail StructBlock with a StreamBlockField
我正在尝试 运行 对由普通块字段和 StreamBlock 组成的 StructBlock 进行一些单元测试。
我 运行 遇到的问题是我可以构建一个渲染块的测试,但我无法测试 StreamBlock 验证(即,我无法测试块的 clean()
)
堆栈
- Python 3.9.6
- Django 3.2
- 鹡鸰2.13
- pytest 6.2
- pytest-django 4.4
块定义
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
以及 clean
和 render
?或者,这种类型的块是否在某种程度上需要一种集成方法,在这种方法中,我构建一个页面并将复杂的块添加为 stream_data
,然后请求该页面并使用响应的呈现?
每个块类型都有一个对应的 'native' 数据类型用于它希望使用的数据 - 对于更简单的块,这种数据类型就是您所期望的(例如,CharBlock 的字符串,图像ImageChooserBlock 的实例),但对于一些更复杂的实例,定义了一个自定义类型:
- 对于 RichTextBlock,原生类型是
wagtail.core.rich_text.RichText
(其行为类似于字符串,但也有一个 source
属性,例如页面链接中的页面 ID 保持不变)
- 对于 StreamBlock,原生类型是
wagtail.core.blocks.StreamValue
(一种序列类型,其中每个项目都是具有 block_type
和 value
属性的 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)
有望成功。
我正在尝试 运行 对由普通块字段和 StreamBlock 组成的 StructBlock 进行一些单元测试。
我 运行 遇到的问题是我可以构建一个渲染块的测试,但我无法测试 StreamBlock 验证(即,我无法测试块的 clean()
)
堆栈
- Python 3.9.6
- Django 3.2
- 鹡鸰2.13
- pytest 6.2
- pytest-django 4.4
块定义
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
以及 clean
和 render
?或者,这种类型的块是否在某种程度上需要一种集成方法,在这种方法中,我构建一个页面并将复杂的块添加为 stream_data
,然后请求该页面并使用响应的呈现?
每个块类型都有一个对应的 'native' 数据类型用于它希望使用的数据 - 对于更简单的块,这种数据类型就是您所期望的(例如,CharBlock 的字符串,图像ImageChooserBlock 的实例),但对于一些更复杂的实例,定义了一个自定义类型:
- 对于 RichTextBlock,原生类型是
wagtail.core.rich_text.RichText
(其行为类似于字符串,但也有一个source
属性,例如页面链接中的页面 ID 保持不变) - 对于 StreamBlock,原生类型是
wagtail.core.blocks.StreamValue
(一种序列类型,其中每个项目都是具有block_type
和value
属性的 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)
有望成功。