Chapter 14 · Forms

14.4 Validators: django/core/validators.py

A validator is a callable that is given one value and raises ValidationError when the value will not do. django/core/validators.py holds the general ones: RegexValidator and the validators of domain names, URLs and email addresses, BaseValidator and the limits built on it, DecimalValidator, FileExtensionValidator, and ready-made ones such as validate_email.

A validator is given the value and nothing else, so a function of one argument serves as well as a class, and one validator serves form and model fields alike.

Each validator here raises with a code, which names the rule broken ("min_value"), and parameters, which fill the message’s placeholders. The message is filled only when it is read, so a field’s own message for the code, put in the validator’s place, can use the same parameters. The berth form’s tonnage, given one:

# harbour/fleet/forms.py
tonnage = forms.IntegerField(
    min_value=1,
    error_messages={"min_value": "%(value)s tons is below the limit of %(limit_value)s."},
)
>>> form = BerthForm({"ship": "heron", "tonnage": "0", "arrives": "2026-10-20"})
>>> form.errors.as_data()
{'tonnage': [ValidationError(['0 tons is below the limit of 1.'])]}

A validator is ordinarily made once, with its module, field class or field, and called many times (validate_image_file_extension makes a new one at each call): a form’s copy of its fields copies each list of validators but not the validators. It holds what it was made with, and nothing between calls.

Where validators are called

A form field’s Field.run_validators (A form field: the base class) and a model field’s (Validating an instance) work alike: they skip an empty value, call every validator of the field, gather all their errors into one ValidationError, and put the field’s own message in place of a validator’s where the field has one for its code.

An empty value is one of the field’s empty_values, which both base classes copy from EMPTY_VALUES: None, "" and an empty list, tuple or dictionary. Whether a field may be empty is its own question; no validator is asked it.

A validator in a migration

Every validator class of the module is decorated with deconstructible, whose deconstruct returns the arguments an instance was made with, so that a migration can write a model field’s validator as a call that makes it again (Serializing values: serializer_factory and deconstruct).

RegexValidator

RegexValidator is the base of the pattern validators. Its class attributes RegexValidator.regex, RegexValidator.message, RegexValidator.code, RegexValidator.inverse_match and RegexValidator.flags are defaults that its arguments replace, so a subclass can be written by setting attributes alone, as the auth app’s username validators are. The pattern is wrapped by _lazy_re_compile in a SimpleLazyObject, and compiled when it is first used.

A call looks for the pattern anywhere in str(value), by re.Pattern.search, and the value is invalid when it is not found or, with inverse_match, when it is. A pattern that must match the whole value anchors itself, as each RegexValidator the module makes does with "^" and "\Z".

Domain names, URLs and email addresses

DomainNameValidator, URLValidator and EmailValidator share one description of a host name, kept on DomainNameValidator: labels of 1 to 63 characters that neither begin nor end with a hyphen, then a top-level domain of letters or a punycode label, "xn--" and the rest. A letter may be any character from U+00A1 to U+FFFF, so bücher.example passes as it is written, and is not converted to punycode.

DomainNameValidator joins the parts when it is made, ASCII-only ones with accept_idna=False, which also refuses a value that is not ASCII. It refuses a value that is not a string or is over 255 characters long, and wants a top-level domain, so "localhost" fails.

URLValidator refuses, in order and each with its code ("invalid" by default), a value that is not a string or is longer than URLValidator.max_length (2,048, MAX_URL_LENGTH); that holds a tab, carriage return, newline or null; whose scheme, before "://" and lower-cased, is not in URLValidator.schemes (by default "http", "https", "ftp" and "ftps"); that urllib.parse.urlsplit cannot split; that fails the pattern, which takes as host an IPv4 address, an IPv6 address in brackets, a host name or "localhost"; whose address in brackets validate_ipv6_address refuses; and whose host name is missing or longer than 253 characters, RFC 1034’s limit.

EmailValidator splits the address and checks its parts in methods a subclass may replace. EmailValidator.parse_address refuses a value that is empty, has no "@" or is longer than 320 characters, and splits it at the last "@". EmailValidator.validate has the user part checked by EmailValidator.validate_username, as a dot-atom or a quoted string, and the domain by EmailValidator.validate_domain, which passes one in the allowlist (by default ["localhost"]), one that matches the host-name pattern without the final dot a URL may have, or an address in brackets that validate_ipv46_address accepts.

Limits: BaseValidator and its subclasses

BaseValidator compares a value with a limit. A call passes the value through BaseValidator.clean, calls the limit if it is callable, and raises if BaseValidator.compare of the two is true, with the parameters "limit_value", "show_value" (the cleaned value) and "value". In the base class clean returns the value and compare is a is not b. MaxValueValidator and MinValueValidator compare the value, and MaxLengthValidator and MinLengthValidator its length; their codes are "max_value", "min_value", "max_length" and "min_length".

StepValueValidator, code "step_size", wants a multiple of the limit, by math.remainder with a tolerance of 1e-9, so that 0.3 is a multiple of 0.1. Given an offset, it counts from there, and its message names the offset and the next two valid values. The form IntegerField makes one from step_size=, with min_value= as the offset.

Decimals, file extensions and null characters

DecimalValidator refuses an infinity or a NaN as "invalid", then too many digits, decimal places or digits before the point, as "max_digits", "max_decimal_places" and "max_whole_digits".

FileExtensionValidator refuses a file whose name’s suffix, lower-cased, is not among the allowed extensions; with none given, it allows all. validate_image_file_extension makes one at each call from the extensions Pillow registers; without Pillow that list is empty and every file is refused. The form ImageField has it by default.

ProhibitNullCharactersValidator refuses a value whose string holds a null character. Every form CharField has one.

The ready-made validators

Table 14.3 The module’s ready-made validators (django/core/validators.py).
name what it is
validate_integer calls integer_validator, a RegexValidator for an optional minus and digits
validate_slug, validate_unicode_slug RegexValidator instances: ASCII letters, digits, "_" and "-"; or Unicode word characters and "-"
validate_email an EmailValidator
validate_domain_name a DomainNameValidator
validate_ipv4_address, validate_ipv6_address, validate_ipv46_address functions; the third tries the other two
ip_address_validators the list of those for "both", "ipv4" or "ipv6", for the IP address fields; ValueError for another
int_list_validator makes a RegexValidator for unsigned integers (signed, if asked) joined by a separator, "," by default, as validate_comma_separated_integer_list is