十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Wagtail StreamField 块如何编写自定义校验并只在发布时强制必填?

Wagtail StreamField 块如何编写自定义校验并只在发布时强制必填? Wagtail StreamField 块如何编写自定义校验并只在发布时强制必填【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail如果你在给 Wagtail 的 StreamField 写自定义块会遇到两类常见诉求一是块里有跨字段规则比如页面和 URL 必须填其一需要写自定义校验二是希望某些必填字段只在发布时强制草稿保存时允许留空而另一些字段则始终必填。从 Wagtail 7.4 起StreamField 块在保存页面草稿时默认采用延迟校验deferred validation必填约束在保存草稿时被跳过在页面或 snippet 发布、计划发布或提交工作流时照常校验。本篇基于 StreamField validation 文档讲清如何用clean方法写自定义校验、如何用required_on_save控制必填时机以及如何让校验错误显示在具体的子块上。校验机制clean 方法与 is_deferred_validation所有 StreamField 块都实现了clean方法它接收块的值返回清理后的值或在值不合法时抛出ValidationError。内置规则如 URLBlock 校验 URL 格式也是通过这个方法实现的。对于 StructBlock 这类容器块clean会递归调用子块的clean并把子块的校验错误向上传递。在保存草稿时执行校验的过程中块实例的is_deferred_validation属性会被设置为True。这个属性就是只在发布时强制的实现依据——在clean里检查它就能决定某条规则是否跳过。机制定义可在 wagtail/blocks/base.py 中的defer_required_validation和clean_deferred方法看到顶层块的clean_deferred会先调用defer_required_validation()置位is_deferred_validation True再调用clean结束后恢复。注意一个适用条件延迟校验默认对页面草稿以及使用DraftStateMixin的 snippet生效见 docs/releases/7.4.md 中的 Deferred validation for StreamField blocks when saving drafts。这是 7.4 引入的默认行为不需要额外配置。第一步用 is_deferred_validation 写发布时才强制的校验官方文档给出的示例是一个 StructBlock规则是page或url在发布时至少填一个但保存草稿时不要求text则任何时候都必填。from django.core.exceptions import ValidationError from wagtail.blocks import StructBlock, PageChooserBlock, URLBlock class LinkBlock(StructBlock): page PageChooserBlock(requiredFalse) url URLBlock(requiredFalse) text CharBlock(required_on_saveTrue) def clean(self, value): result super().clean(value) if not self.is_deferred_validation and not (result[page] or result[url]): raise ValidationError(Either page or URL must be specified) return result各部分的写法说明先调用super().clean(value)让子块完成各自的标准校验拿到清理后的result再执行自己的跨字段规则if not self.is_deferred_validation and ...是关键is_deferred_validation为True保存草稿时整条规则被跳过只有发布等场景下page和url都为空时才抛错text CharBlock(required_on_saveTrue)表示text在保存草稿时也要执行必填校验与自定义规则形成对比。required_on_save是字段块FieldBlock的选项默认值为False见 docs/reference/streamfield/blocks.md 中 Field block types 一节。设为True后该字段的必填约束在保存草稿时不会被告免。实现上FieldBlock.defer_required_validation会把底层表单字段的required恢复为required_on_save的值见 wagtail/blocks/field_block.py 第 66-69 行普通必填字段在草稿态被置为requiredFalse而带required_on_saveTrue的字段保持必填。第二步控制错误信息渲染在哪个块上默认情况下在clean中抛出ValidationError会把错误挂在 StructBlock 整体上进行渲染。如果希望错误显示在具体的子块上改用wagtail.blocks.StructBlockValidationError其构造函数接受两个参数non_block_errors挂在 StructBlock 整体上的错误消息列表或ValidationError实例列表block_errors以子块名称为键、ValidationError实例为值的字典错误将显示在对应子块上。例如把描述必须包含关键词的报错挂在description子块上from django.core.exceptions import ValidationError from wagtail.blocks import CharBlock, StructBlock, StructBlockValidationError, TextBlock class TopicBlock(StructBlock): keyword CharBlock() description TextBlock() def clean(self, value): result super().clean(value) if result[keyword] not in result[description]: raise StructBlockValidationError( block_errors{ description: ValidationError( Description must contain the keyword ) } ) return resultListBlock 和 StreamBlock 有对应的异常类wagtail.blocks.ListBlockValidationError和wagtail.blocks.StreamBlockValidationError用法相同区别是block_errors字典的键为块的数字索引。文档给出的示例是一个数值必须升序的 ListBlockfrom django.core.exceptions import ValidationError from wagtail.blocks import ListBlock, ListBlockValidationError class AscendingListBlock(ListBlock): # example usage: # price_list AscendingListBlock(FloatBlock()) def clean(self, value): result super().clean(value) errors {} for i in range(1, len(result)): if result[i] result[i - 1]: errors[i] ValidationError(Values must be in ascending order) if errors: raise ListBlockValidationError(block_errorserrors) return result如何验证行为可以在管理界面按场景验证编辑一个含上述块的页面保存草稿时只填text而page/url留空应能正常保存且text留空会立即报错执行发布时page/url均为空则显示 Either page or URL must be specified。仓库测试代码 wagtail/tests/test_blocks.py 中的test_required_on_save也给出了直接的行为断言方式blocks.CharBlock(required_on_saveTrue)上无论是延迟校验路径block.clean_deferred()还是普通路径block.clean()空值都会抛出ValidationError而未设置该选项的CharBlock只会在clean()时抛错clean_deferred()通过。如果你的块是嵌套在 StreamBlock 里使用的is_deferred_validation由顶层块的clean_deferred统一置位并传播到子块无需在子块上重复处理。限制模型级校验方法不会检查块文档特别指出StreamField 中块的校验发生在表单字段wagtail.blocks.base.BlockField上而不是模型字段wagtail.fields.StreamField上。因此对页面实例调用my_page.full_clean()这类模型级校验方法捕获不到 StreamField 数据中的非法块。这个限制只在 StreamField 数据绕过表单字段、通过程序化方式写入时才构成实际问题只要数据经过管理界面的表单提交校验都会正常执行。小结完整路径是在块子类中重写clean并先调用super().clean(value)用is_deferred_validation决定哪些规则只在发布时强制对需要草稿也必填的字段块设置required_on_saveTrue用StructBlockValidationError/ListBlockValidationError/StreamBlockValidationError把错误定位到具体子块。机制入口分别是 docs/advanced_topics/streamfield_validation.md本文依据的文档和 wagtail/blocks/base.py 中的defer_required_validation/clean_deferred。【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表