1from __future__ import annotations
 2
 3from typing import Any
 4
 5from plain import exceptions
 6
 7from .base import DefaultableField
 8
 9
10class BooleanField(DefaultableField[bool]):
11    db_type_sql = "boolean"
12    empty_strings_allowed = False
13
14    def to_python(self, value: Any) -> bool | None:
15        if self.allow_null and value in self.empty_values:
16            return None
17        if value in (True, False):
18            # 1/0 are equal to True/False. bool() converts former to latter.
19            return bool(value)
20        if value in ("t", "True", "1"):
21            return True
22        if value in ("f", "False", "0"):
23            return False
24        if self.allow_null:
25            message = '"%(value)s" value must be either True, False, or None.'
26        else:
27            message = '"%(value)s" value must be either True or False.'
28        raise exceptions.ValidationError(
29            message,
30            code="invalid",
31            params={"value": value},
32        )
33
34    def get_prep_value(self, value: Any) -> Any:
35        value = super().get_prep_value(value)
36        if value is None:
37            return None
38        return self.to_python(value)