Skip to content

Validators

Introduction

As Reforms expands Pydantic, then you can use all existed features including validators. Here you can see a simple example, for more information please go to the appropriate page of the pydantic documentation.

from pydantic import BaseModel, ValidationError, validator
from reforms import BooleanField, StringField


class UserModel(BaseModel):
    name: StringField()
    is_admin: BooleanField()
    username: StringField()
    password1: StringField()
    password2: StringField()

    @validator("name")
    def name_must_contain_space(cls, v):
        if " " not in v:
            raise ValueError("must contain a space")
        return v.title()

    @validator("password2")
    def passwords_match(cls, v, values):
        if "password1" in values and v != values["password1"]:
            raise ValueError("passwords do not match")
        return v

    @validator("username")
    def username_alphanumeric(cls, v):
        assert v.isalnum(), "must be alphanumeric"
        return v

    @validator("is_admin")
    def contains_admin_name(cls, v, values):
        if v and ("name" not in values or "admin" not in values["name"].lower()):
            raise ValueError('if user is admin, his name must contain "admin" part')

        return v


user = UserModel(
    name="samuel colvin admin",
    username="scolvin",
    password1="zxcvbn",
    password2="zxcvbn",
    is_admin=True,
)
print(user)

try:
    UserModel(
        name="samuel",
        username="scolvin",
        password1="zxcvbn",
        password2="zxcvbn2",
        is_admin=True,
    )
except ValidationError as e:
    print(e)
(This script is complete, it should run "as is")

Built-in validators

If you used wtforms before, you have definitely used their built-in validators and implemented yours owns in cases, when you needed some custom behavior. Reforms also contains a similar validators list. Here is a simple example.

from pydantic import BaseModel, ValidationError
from reforms import StringField
from reforms.validators import Length


class ContactUsModel(BaseModel):
    name: StringField(validators=[Length(min=5)])
    message: StringField()


contact = ContactUsModel(name="Roman", message="Some message")
print(contact)

try:
    ContactUsModel(
        message="Temp",
    )
except ValidationError as e:
    print(e)

try:
    ContactUsModel(
        name="Dan",
    )
except ValidationError as e:
    print(e)
(This script is complete, it should run "as is")

All current built-in validators you can see below.

Length

validates the length of a string.
  • min: int =- 1: The minimum required length of the string. If not provided, minimum length will not be checked.
  • max: int =- 1: The maximum length of the string. If not provided, maximum length will not be checked.
  • message: str = "": Error message to raise in case of a validation error. Can be interpolated using {min} and {max} if desired. Useful defaults are provided depending on the existence of min and max.

AnyOf

compares the incoming data to a sequence of valid inputs.
  • values: Sequence[Any]: A sequence of valid inputs.
  • message: str = "Invalid value, must be one of: {values}.": Error message to raise in case of a validation error. {values} contains the list of values.
  • values_formatter: Callable = None: Function used to format the list of values in the error message.

NoneOf

compares the incoming data to a sequence of invalid inputs.
  • values: Sequence[Any]: A sequence of valid inputs.
  • message: str = "Invalid value, must be one of: {values}.": Error message to raise in case of a validation error. {values} contains the list of values.
  • values_formatter: Callable = None: Function used to format the list of values in the error message.

Write your custom validator

To write custom validator, you need to create a callable object (function or class with implemented __call__ method) with the parameters below.

Warning

Validators should either return the parsed value or raise a ValueError, TypeError, or AssertionError (assert statements may be used). It's the same as with default pydantic validators.

Parameter Type Description
value Any Current field value
field (Optional) ModelField Parameter to get access to the field itself

from typing import Callable

from pydantic import BaseModel, ValidationError
from reforms import StringField


def has_lines(n: int = 2) -> Callable:
    def _has_lines(value: str):
        if len(value.split("\n")) < n:
            raise ValueError(f"Value doesn't contain minimum {n} lines")

        return value

    return _has_lines


class MessageModel(BaseModel):
    content: StringField(validators=[has_lines(n=3)])


contact = MessageModel(
    content="""
First sentence.
Second sentence.
Third sentence"""
)

print(contact)

try:
    MessageModel(content="One line")
except ValidationError as e:
    print(e)
(This script is complete, it should run "as is")