Validators and filters#
Validators and filters both process field values, but they run at different points in the pipeline and serve different purposes.
Validators#
A validator is a function that checks whether a user's answer meets a constraint. It runs at prompt time, immediately after the user submits an answer.
Input: the raw string the user typed.
Output:
bool(Truemeans the answer is accepted;Falsemeans the prompt repeats).Effect: a rejected answer causes Cookieplone to ask the question again until the value is valid.
Validators enforce correctness—they prevent invalid data from entering the template context.
Example#
The python_package_name validator checks that the value is a valid Python identifier or dotted name:
def python_package_name(value: str) -> bool:
"""Validate python_package_name is an identifier."""
result = validators.validate_python_package_name(value)
return not result
If you type my-addon (with a hyphen), the prompt repeats.
If you type my_addon, the answer is accepted and the wizard moves on.
Autowiring with DEFAULT_VALIDATORS#
Cookieplone maintains a table of field names mapped to validators. Any field whose name appears in this table is validated automatically—no configuration in the template schema is needed.
Field name |
Validator applied |
|---|---|
|
|
|
|
|
|
|
|
|
|
A template can override any autowired validator by providing an explicit validator key in cookieplone.json.
Filters#
A filter is a Jinja2 function that transforms a value. It runs at render time: after the wizard has collected all answers, when Cookiecutter renders the template files.
Input: any value already in the template context.
Output: a transformed value (string, integer, or list).
Effect: the rendered output in files, filenames, or computed defaults reflects the transformed value.
Filters shape data—they convert raw answers into the exact form needed in the generated output.
Example#
The pascal_case filter converts an underscore-separated name to PascalCase:
@simple_filter
def pascal_case(package_name: str) -> str:
"""Return the package name as a string in the PascalCase format."""
parts = [name.title() for name in package_name.split("_")]
return "".join(parts)
In a template file:
# {{ cookiecutter.python_package_name | pascal_case }}
If python_package_name is collective.myaddon, the rendered line becomes:
# Collective.Myaddon
Key differences#
Aspect |
Validator |
Filter |
|---|---|---|
When it runs |
Prompt time (after user types) |
Render time (after all answers collected) |
Input |
Raw user input string |
Any template context value |
Output |
|
Transformed value |
Purpose |
Reject invalid answers |
Convert values for use in files |
Configured in |
|
` |
User sees result? |
Indirectly (rejected prompts) |
In generated files |
Using both together#
Validators and filters often work together on the same field.
The python_package_name field uses python_package_name (validator) to ensure the value is a valid Python identifier,
then uses pascal_case (filter) in computed fields and template files to derive a class name:
{
"python_package_name": {
"type": "string",
"title": "Python package name",
"default": "collective.myaddon"
},
"class_name": {
"type": "string",
"format": "computed",
"default": "{{ cookiecutter.python_package_name | pascal_case }}"
}
}
The validator runs first (at prompt time) to ensure the value is valid. The filter runs later (at render time) to produce the derived class name.