Input validation
There are two types of input validation:
- generic: automatically detected by the platform during user input
- specific: custom logic to handle more complex validation
Both types provide the possibility to mark fields invalid in the interface, which helps users to correct input mistakes.
Generic field constraints
The following constraints are automatically assessed by the platform:
When any of these are violated, all actions are blocked until the user fixes the fields (invisible fields are not considered). This way it can be ensured that wrong inputs are never used in one of the calculations. The actions that are blocked are:
- action buttons
- view calculations
- next-button on a Step
Constraints do only block actions when the controller flag viktor_enforce_field_constraints
is set to True.
This will become default behavior in the future (see U83 for more detail).
The platform currently does not automatically invalidate:
- a selected entity that has been deleted
- a selected file that has been deleted
Numeric min/max boundary
Input value is not within the configured min/max bounds:
NumberField("Number", min=10, max=20) # also on IntegerField / DynamicArray
Non-existing option
The selected option is no longer available, for example due to dynamic options:
OptionField("Please select...", options=dynamic) # also on MultiSelectField / AutocompleteField
Non-existing coordinate
Invalid latitude / longitude pair in a GeoPointField
:
Invalid date format
Format in a DateField
which does not adhere to YYYY-MM-DD:
Specific input validation
Custom logic can be implemented in the application code to handle specific or more complex validation. This is achieved
by means of raising a UserError
, accompanied by the input violations:
from viktor.errors import UserError, InputViolation
def calculate_block_volume(params):
width = params.width
length = params.length
height = params.height
violations = []
if width is None:
violations.append(InputViolation("Input 'width' cannot be empty!", fields=['width']))
if length is None:
violations.append(InputViolation("Input 'length' cannot be empty!", fields=['length']))
if height is None:
violations.append(InputViolation("Input 'height' cannot be empty!", fields=['height']))
if violations:
raise UserError("Cannot calculate block volume", input_violations=violations)
return width * length * height
A UserError
can be raised on the following actions:
- action buttons
- view calculations
- next-button on a Step
The message defined in the UserError
functions as a generic message to the user, while the message in the
InputViolation
is shown on the field itself and can be more specific:
Multiple fields can be passed to the InputViolation
if the message holds for all of them:
violations.append(InputViolation("Input cannot be empty!", fields=['width', 'length', 'height']))
In case there are multiple issues with a field, multiple messages can be provided:
violations.append(InputViolation("Message A", fields=['field']))
violations.append(InputViolation("Message B", fields=['field']))
violations.append(InputViolation("Message C", fields=['field']))
Be careful with marking fields invalid which have dynamic visibility. A user will not be able to fix an invisible field and might be stuck in the validation process!