验证流域中没有重复项

时间:2017-12-11 20:07:29

标签: django wagtail

需要使用侧边栏导航创建一个长滚动页面以进行页面导航。这意味着每个标题都有一个id。我可以在流场级别验证以确保用户没有输入重复的ID吗?

编辑:
标题定义如下:

class HeadingOneBlock(blocks.StructBlock):
    id = blocks.RegexBlock(regex=r'^\S*$', help_text='No spaces or special characters')
    heading = blocks.CharBlock()

    class Meta:
        template = 'base/heading_one.html'
        icon = 'title'
        label = 'h1'

页面:

class LongScrollPage(Page):
    banner_text = RichTextField()
    description = RichTextField()
    body = StreamField([
        ('heading1', HeadingOneBlock()),
        ('heading2', HeadingTwoBlock()),
        ('paragraph', blocks.RichTextBlock(features=['bold', 'italic', 'ol', 'ul', 'link', 'image', 'hr'])),
        ('collapsible_panel', CollapsiblePanelBlock()),
        ('table', TableBlock(template='base/tables.html')),
    ], blank=True, null=True)

    content_panels = Page.content_panels + [
        FieldPanel('banner_text'),
        FieldPanel('description'),
        StreamFieldPanel('body'),
    ]

有没有办法在“身体”级别进行验证?我知道我可以在块级别进行验证,但是如何确保ID是唯一的?

3 个答案:

答案 0 :(得分:1)

根据wagtail的作者,您显然not supposed to access stream_data directly就是这种方式,但是可以稍微修改上面的代码以获得相同的效果。

def validate_ids_in_headings(value):
# value.stream_data is a list of tuples
# first item in each tuple is the label (eg. 'heading1' or 'paragraph')
# second item in each tuple is a StructValue which is like an ordered dict
items = [
    data.value.get('id')
    for data in value
]
if len(set(items)) != len(items):
    # sets can only be unique so - the items are are not unique
    # must raise special StreamBlockValidationError error like this
    raise StreamBlockValidationError(
        non_block_errors=ValidationError(
            'All headings must have unique ID values: %(value)s',
            code='invalid',
            params={'value': items},
        )
    )
# if valid, do nothing

我认为这应该照顾您的情况,并且它不依赖于不应直接访问的字段。

答案 1 :(得分:0)

解决此问题的一种方法是使用validators上的自定义StreamField属性。

您可以阅读有关Django Form Validation的更多信息。

此外,提供给验证程序的value[StreamValue](https://github.com/wagtail/wagtail/blob/master/wagtail/core/blocks/stream_block.py#L324)的实例,其具有属性stream_data,其中包含元组列表。每个元组中的第一项是您给出块的标签(例如,示例中的heading1,heading2,段落)。第二项是StructValue实例,其中每个子块值可以通过其键hence data[1].get('id')在下面的示例中访问。

验证码(也可以在models.py文件中):

from django.core.exceptions import ValidationError
from wagtail.core.blocks import StreamBlockValidationError # plus all blocks imported here


def validate_ids_in_headings(value):
    # value.stream_data is a list of tuples
    # first item in each tuple is the label (eg. 'heading1' or 'paragraph')
    # second item in each tuple is a StructValue which is like an ordered dict
    items = [
        data[1].get('id')
        for data in value.stream_data
        if 'heading' in data[0]
    ]
    if len(set(items)) != len(items):
        # sets can only be unique so - the items are are not unique
        # must raise special StreamBlockValidationError error like this
        raise StreamBlockValidationError(
            non_block_errors=ValidationError(
                'All headings must have unique ID values: %(value)s',
                code='invalid',
                params={'value': items},
            )
        )
    # if valid, do nothing

修订的型号代码:

# using one HeadingBlock class here as you can define template/label within the StreamField
class HeadingBlock(StructBlock):
    id = RegexBlock(regex=r'^\S*$', help_text='No spaces or special characters')
    heading = CharBlock()
    class Meta:
        icon = 'title'


body = StreamField([
    ('heading1', HeadingBlock(label='h1', template='base/heading_one.html')), # note: label/template set here
    ('heading2', HeadingBlock(label='h2', template='base/heading_two.html')), # note: label/template set here
    ('paragraph', RichTextBlock(features=['bold', 'italic', 'ol', 'ul', 'link', 'image', 'hr'])),
    ('collapsible_panel', CollapsiblePanelBlock()),
    ('table', TableBlock(template='base/tables.html')),
 # validators set here, must be within a list - even if one item, removed null=True as not needed
], blank=True, validators=[validate_ids_in_headings])

这将向用户显示错误。

不幸的是,这不会突出显示标签(带有错误指示符),但它会在“正文”块的开头显示错误,并显示特定错误文本Issue 4122

我在使用Python 3的BakeryDemo,Wagtail 1.13上测试了这个。

答案 2 :(得分:0)

对于 wagtail 2.11.8 版,这对我有用。 (这是对之前答案的修改)

"""Streamfield uniqueness validator"""
def streamfield_uniqueness_validator(value):
    # value.stream_data is a list of tuples
    # first item in each tuple is the label (eg. 'recipient_email')
    # second item in each tuple is the actual value of field 
    # third item in each tuple is the id of tuple or dict
    items = [
        data[1].lower()
        for data in value.stream_data
    ]
    print(items)
    if len(set(items)) != len(items):
        # sets can only be unique so - the items are are not unique
        # must raise special StreamBlockValidationError error like this
        raise StreamBlockValidationError(
            non_block_errors=ValidationError(
                'Cannot add same email on multiple fields: %(value)s',
                code='invalid',
                params={'value': items},
            )
        )

在我的模型中:

class ModelName(models.Model):
    stream_fileds = StreamField([('stream_filed', blocks.CharBlock())], validators=[streamfield_uniqueness_validator])