Add validators to your template#
A validator checks an answer before Cookieplone accepts it.
In the wizard, a rejected answer shows an error, and Cookieplone asks the question again.
With --no-input, Cookieplone validates the values it would use instead, and a rejected value stops the generation.
The examples on this page come from the example repository docs/_examples/template-features/ in the Cookieplone source tree.
Use a built-in validator#
In your template's cookieplone.json, set the validator key of a property to the dotted import path of a validator:
{
"author_email": {
"type": "string",
"title": "Author email",
"default": "jane@example.com",
"validator": "cookieplone.validators.not_empty"
}
}
Cookieplone provides validators for non-empty values, Python package names, npm package names, Volto add-on names, hostnames, language codes, and Plone and Volto versions. See Validators reference for the list.
Rely on automatic validators#
Some field names get a validator without any configuration.
Name a property python_package_name, and Cookieplone applies cookieplone.validators.python_package_name to it:
{
"python_package_name": {
"type": "string",
"title": "Python package name",
"default": "collective.example_addon"
}
}
The same happens for properties named plone_version, volto_version, hostname, and language_code.
A validator key on the property replaces the automatic validator.
Write a custom validator#
Create a Python module in your template's directory, next to its
cookieplone.json:"""Validators for the example template.""" from tui_forms.form.question import ValidationError def no_spaces(value: str) -> bool: """Reject answers that contain spaces.""" if " " in value: raise ValidationError("Use hyphens instead of spaces.") return True
The function receives the answer as a string. It returns
Trueto accept the answer, or raisesValidationErrorwith a message that tells the user what to fix. See Validator contract for the complete contract.Reference the function by its module and function name:
{ "project_slug": { "type": "string", "title": "Project slug", "default": "example-addon", "validator": "my_validators.no_spaces" } }
Cookieplone adds the template's directory to the Python import path while it generates the template, so it finds my_validators there.
The directory goes at the end of the import path, so pick a module name that no installed package uses.
To share validators between templates or repositories, publish them in a Python package, and install that package next to Cookieplone:
uvx --with my-validators cookieplone
Check the result#
With --no-input, Cookieplone runs the validators on the defaults and on any value you pass as extra context.
A rejected value stops the generation with the validator's message:
COOKIEPLONE_REPOSITORY=./my-templates uvx cookieplone features "project_slug=example addon" --no-input
The error includes Use hyphens instead of spaces.
The complete cookieplone.json of the example template declares an explicit validator, a custom validator, and a property that gets an automatic validator:
{
"id": "features",
"schema": {
"title": "Template features",
"description": "Validators, computed fields, and filters.",
"version": "2.0",
"properties": {
"project_title": {
"type": "string",
"title": "Project title",
"default": "Example Add-on"
},
"project_slug": {
"type": "string",
"title": "Project slug",
"default": "example-addon",
"validator": "my_validators.no_spaces"
},
"python_package_name": {
"type": "string",
"title": "Python package name",
"default": "collective.example_addon"
},
"author_email": {
"type": "string",
"title": "Author email",
"default": "jane@example.com",
"validator": "cookieplone.validators.not_empty"
},
"has_docs": {
"type": "boolean",
"title": "Add documentation?",
"default": true
},
"package_path": {
"type": "string",
"format": "computed",
"default": "{{ cookiecutter.python_package_name | package_path }}"
},
"module_path": {
"type": "string",
"format": "computed",
"default": "src/{{ cookiecutter.package_path }}"
},
"class_name": {
"type": "string",
"format": "computed",
"default": "{{ cookiecutter.python_package_name | package_name | pascal_case }}"
},
"docs_enabled": {
"type": "string",
"format": "computed",
"default": "{{ '1' if cookiecutter.has_docs else '0' }}"
},
"generator": {
"type": "string",
"format": "constant",
"default": "example-templates 1.0"
}
}
},
"config": {
"extensions": [
"cookieplone.filters.package_name",
"cookieplone.filters.package_namespace",
"cookieplone.filters.package_path",
"cookieplone.filters.pascal_case"
]
}
}