Plain is headed towards 1.0! Subscribe for development updates →

   1from __future__ import annotations
   2
   3import collections.abc
   4import copy
   5import datetime
   6import decimal
   7import enum
   8import operator
   9import uuid
  10import warnings
  11from base64 import b64decode, b64encode
  12from collections.abc import Callable, Sequence
  13from functools import cached_property, total_ordering
  14from typing import TYPE_CHECKING, Any, Generic, Protocol, Self, TypeVar, cast, overload
  15
  16from plain import exceptions, validators
  17from plain.models.constants import LOOKUP_SEP
  18from plain.models.db import db_connection
  19from plain.models.enums import ChoicesMeta
  20from plain.models.query_utils import RegisterLookupMixin
  21from plain.preflight import PreflightResult
  22from plain.utils import timezone
  23from plain.utils.datastructures import DictWrapper
  24from plain.utils.dateparse import (
  25    parse_date,
  26    parse_datetime,
  27    parse_duration,
  28    parse_time,
  29)
  30from plain.utils.duration import duration_microseconds, duration_string
  31from plain.utils.functional import Promise
  32from plain.utils.ipv6 import clean_ipv6_address
  33from plain.utils.itercompat import is_iterable
  34
  35from ..registry import models_registry
  36
  37if TYPE_CHECKING:
  38    from plain.models.backends.base.base import BaseDatabaseWrapper
  39    from plain.models.base import Model
  40    from plain.models.fields.reverse_related import ForeignObjectRel
  41    from plain.models.sql.compiler import SQLCompiler
  42
  43__all__ = [
  44    "BLANK_CHOICE_DASH",
  45    "PrimaryKeyField",
  46    "BigIntegerField",
  47    "BinaryField",
  48    "BooleanField",
  49    "CharField",
  50    "DateField",
  51    "DateTimeField",
  52    "DecimalField",
  53    "DurationField",
  54    "EmailField",
  55    "Empty",
  56    "Field",
  57    "FloatField",
  58    "GenericIPAddressField",
  59    "IntegerField",
  60    "NOT_PROVIDED",
  61    "PositiveBigIntegerField",
  62    "PositiveIntegerField",
  63    "PositiveSmallIntegerField",
  64    "SmallIntegerField",
  65    "TextField",
  66    "TimeField",
  67    "URLField",
  68    "UUIDField",
  69]
  70
  71
  72class Empty:
  73    pass
  74
  75
  76class NOT_PROVIDED:
  77    pass
  78
  79
  80# The values to use for "blank" in SelectFields. Will be appended to the start
  81# of most "choices" lists.
  82BLANK_CHOICE_DASH = [("", "---------")]
  83
  84
  85def _load_field(
  86    package_label: str, model_name: str, field_name: str
  87) -> Field[Any] | ForeignObjectRel:
  88    return models_registry.get_model(package_label, model_name)._model_meta.get_field(
  89        field_name
  90    )
  91
  92
  93# A guide to Field parameters:
  94#
  95#   * name:      The name of the field specified in the model.
  96#   * attname:   The attribute to use on the model object. This is the same as
  97#                "name", except in the case of ForeignKeys, where "_id" is
  98#                appended.
  99#   * db_column: The db_column specified in the model (or None).
 100#   * column:    The database column for this field. This is the same as
 101#                "attname", except if db_column is specified.
 102#
 103# Code that introspects values, or does other dynamic things, should use
 104# attname.
 105
 106
 107def _empty(of_cls: type) -> Empty:
 108    new = Empty()
 109    new.__class__ = of_cls
 110    return new
 111
 112
 113def return_None() -> None:
 114    return None
 115
 116
 117# TypeVar for Field's generic type parameter
 118T = TypeVar("T")
 119
 120
 121@total_ordering
 122class Field(RegisterLookupMixin, Generic[T]):
 123    """Base class for all field types"""
 124
 125    # Instance attributes set during field lifecycle
 126    # Set by __init__
 127    name: str | None
 128    max_length: int | None
 129    db_column: str | None
 130    # Set by set_attributes_from_name (called by contribute_to_class)
 131    attname: str
 132    column: str
 133    concrete: bool
 134    # Set by contribute_to_class
 135    model: type[Model]
 136
 137    # Designates whether empty strings fundamentally are allowed at the
 138    # database level.
 139    empty_strings_allowed = True
 140    empty_values = list(validators.EMPTY_VALUES)
 141
 142    # These track each time a Field instance is created. Used to retain order.
 143    # The auto_creation_counter is used for fields that Plain implicitly
 144    # creates, creation_counter is used for all user-specified fields.
 145    creation_counter: int = 0
 146    auto_creation_counter: int = -1
 147    default_validators = []  # Default set of validators
 148    default_error_messages = {
 149        "invalid_choice": "Value %(value)r is not a valid choice.",
 150        "allow_null": "This field cannot be null.",
 151        "required": "This field is be required.",
 152        "unique": "A %(model_name)s with this %(field_label)s already exists.",
 153    }
 154
 155    # Attributes that don't affect a column definition.
 156    # These attributes are ignored when altering the field.
 157    non_db_attrs = (
 158        "required",
 159        "choices",
 160        "db_column",
 161        "error_messages",
 162        "limit_choices_to",
 163        # Database-level options are not supported, see #21961.
 164        "on_delete",
 165        "related_query_name",
 166        "validators",
 167    )
 168
 169    # Generic field type description, usually overridden by subclasses
 170    def _description(self) -> str:
 171        return f"Field of type: {self.__class__.__name__}"
 172
 173    description = property(_description)
 174
 175    def __init__(
 176        self,
 177        *,
 178        max_length: int | None = None,
 179        required: bool = True,
 180        allow_null: bool = False,
 181        default: Any = NOT_PROVIDED,
 182        choices: Any = None,
 183        db_column: str | None = None,
 184        validators: Sequence[Callable[..., Any]] = (),
 185        error_messages: dict[str, str] | None = None,
 186        db_comment: str | None = None,
 187    ):
 188        self.name = None  # Set by set_attributes_from_name
 189        self.max_length = max_length
 190        self.required, self.allow_null = required, allow_null
 191        self.default = default
 192        if isinstance(choices, ChoicesMeta):
 193            choices = choices.choices
 194        elif isinstance(choices, enum.EnumMeta):
 195            choices = [(member.value, member.name) for member in choices]
 196        if isinstance(choices, collections.abc.Iterator):
 197            choices = list(choices)
 198        self.choices = choices
 199        self.db_column = db_column
 200        self.db_comment = db_comment
 201
 202        self.primary_key = False
 203        self.auto_created = False
 204
 205        # Adjust the appropriate creation counter, and save our local copy.
 206        self.creation_counter = Field.creation_counter
 207        Field.creation_counter += 1
 208
 209        self._validators = list(validators)  # Store for deconstruction later
 210
 211        self._error_messages = error_messages  # Store for deconstruction later
 212
 213    def __str__(self) -> str:
 214        """
 215        Return "package_label.model_label.field_name" for fields attached to
 216        models.
 217        """
 218        if not hasattr(self, "model"):
 219            return super().__str__()
 220        model = self.model
 221        return f"{model.model_options.label}.{self.name}"
 222
 223    def __repr__(self) -> str:
 224        """Display the module, class, and name of the field."""
 225        path = f"{self.__class__.__module__}.{self.__class__.__qualname__}"
 226        name = getattr(self, "name", None)
 227        if name is not None:
 228            return f"<{path}: {name}>"
 229        return f"<{path}>"
 230
 231    def preflight(self, **kwargs: Any) -> list[PreflightResult]:
 232        return [
 233            *self._check_field_name(),
 234            *self._check_choices(),
 235            *self._check_db_comment(),
 236            *self._check_null_allowed_for_primary_keys(),
 237            *self._check_backend_specific_checks(),
 238            *self._check_validators(),
 239        ]
 240
 241    def _check_field_name(self) -> list[PreflightResult]:
 242        """
 243        Check if field name is valid, i.e. 1) does not end with an
 244        underscore, 2) does not contain "__" and 3) is not "id".
 245        """
 246        assert self.name is not None, "Field name must be set before checking"
 247        if self.name.endswith("_"):
 248            return [
 249                PreflightResult(
 250                    fix="Field names must not end with an underscore.",
 251                    obj=self,
 252                    id="fields.name_ends_with_underscore",
 253                )
 254            ]
 255        elif LOOKUP_SEP in self.name:
 256            return [
 257                PreflightResult(
 258                    fix=f'Field names must not contain "{LOOKUP_SEP}".',
 259                    obj=self,
 260                    id="fields.name_contains_lookup_separator",
 261                )
 262            ]
 263        elif self.name == "id":
 264            return [
 265                PreflightResult(
 266                    fix="'id' is a reserved word that cannot be used as a field name.",
 267                    obj=self,
 268                    id="fields.reserved_field_name_id",
 269                )
 270            ]
 271        else:
 272            return []
 273
 274    @classmethod
 275    def _choices_is_value(cls, value: Any) -> bool:
 276        return isinstance(value, str | Promise) or not is_iterable(value)
 277
 278    def _check_choices(self) -> list[PreflightResult]:
 279        if not self.choices:
 280            return []
 281
 282        if not is_iterable(self.choices) or isinstance(self.choices, str):
 283            return [
 284                PreflightResult(
 285                    fix="'choices' must be an iterable (e.g., a list or tuple).",
 286                    obj=self,
 287                    id="fields.choices_not_iterable",
 288                )
 289            ]
 290
 291        choice_max_length = 0
 292        # Expect [group_name, [value, display]]
 293        for choices_group in self.choices:
 294            try:
 295                group_name, group_choices = choices_group
 296            except (TypeError, ValueError):
 297                # Containing non-pairs
 298                break
 299            try:
 300                if not all(
 301                    self._choices_is_value(value) and self._choices_is_value(human_name)
 302                    for value, human_name in group_choices
 303                ):
 304                    break
 305                if self.max_length is not None and group_choices:
 306                    choice_max_length = max(
 307                        [
 308                            choice_max_length,
 309                            *(
 310                                len(value)
 311                                for value, _ in group_choices
 312                                if isinstance(value, str)
 313                            ),
 314                        ]
 315                    )
 316            except (TypeError, ValueError):
 317                # No groups, choices in the form [value, display]
 318                value, human_name = group_name, group_choices
 319                if not self._choices_is_value(value) or not self._choices_is_value(
 320                    human_name
 321                ):
 322                    break
 323                if self.max_length is not None and isinstance(value, str):
 324                    choice_max_length = max(choice_max_length, len(value))
 325
 326            # Special case: choices=['ab']
 327            if isinstance(choices_group, str):
 328                break
 329        else:
 330            if self.max_length is not None and choice_max_length > self.max_length:
 331                return [
 332                    PreflightResult(
 333                        fix="'max_length' is too small to fit the longest value "  # noqa: UP031
 334                        "in 'choices' (%d characters)." % choice_max_length,
 335                        obj=self,
 336                        id="fields.max_length_too_small_for_choices",
 337                    ),
 338                ]
 339            return []
 340
 341        return [
 342            PreflightResult(
 343                fix="'choices' must be an iterable containing "
 344                "(actual value, human readable name) tuples.",
 345                obj=self,
 346                id="fields.choices_invalid_format",
 347            )
 348        ]
 349
 350    def _check_db_comment(self) -> list[PreflightResult]:
 351        if not self.db_comment:
 352            return []
 353        errors = []
 354        if not (
 355            db_connection.features.supports_comments
 356            or "supports_comments" in self.model.model_options.required_db_features
 357        ):
 358            errors.append(
 359                PreflightResult(
 360                    fix=f"{db_connection.display_name} does not support comments on "
 361                    f"columns (db_comment).",
 362                    obj=self,
 363                    id="fields.db_comment_unsupported",
 364                    warning=True,
 365                )
 366            )
 367        return errors
 368
 369    def _check_null_allowed_for_primary_keys(self) -> list[PreflightResult]:
 370        if self.primary_key and self.allow_null:
 371            # We cannot reliably check this for backends like Oracle which
 372            # consider NULL and '' to be equal (and thus set up
 373            # character-based fields a little differently).
 374            return [
 375                PreflightResult(
 376                    fix="Primary keys must not have allow_null=True. "
 377                    "Set allow_null=False on the field, or "
 378                    "remove primary_key=True argument.",
 379                    obj=self,
 380                    id="fields.primary_key_allows_null",
 381                )
 382            ]
 383        else:
 384            return []
 385
 386    def _check_backend_specific_checks(self) -> list[PreflightResult]:
 387        errors = []
 388        errors.extend(db_connection.validation.check_field(self))
 389        return errors
 390
 391    def _check_validators(self) -> list[PreflightResult]:
 392        errors = []
 393        for i, validator in enumerate(self.validators):
 394            if not callable(validator):
 395                errors.append(
 396                    PreflightResult(
 397                        fix=(
 398                            "All 'validators' must be callable. "
 399                            f"validators[{i}] ({repr(validator)}) isn't a function or "
 400                            "instance of a validator class."
 401                        ),
 402                        obj=self,
 403                        id="fields.invalid_validator",
 404                    )
 405                )
 406        return errors
 407
 408    def get_col(self, alias: str | None, output_field: Field | None = None) -> Any:
 409        if alias == self.model.model_options.db_table and (
 410            output_field is None or output_field == self
 411        ):
 412            return self.cached_col
 413        from plain.models.expressions import Col
 414
 415        return Col(alias, self, output_field)
 416
 417    @cached_property
 418    def cached_col(self) -> Any:
 419        from plain.models.expressions import Col
 420
 421        return Col(self.model.model_options.db_table, self)
 422
 423    def select_format(
 424        self, compiler: SQLCompiler, sql: str, params: Any
 425    ) -> tuple[str, Any]:
 426        """
 427        Custom format for select clauses. For example, GIS columns need to be
 428        selected as AsText(table.col) on MySQL as the table.col data can't be
 429        used by Plain.
 430        """
 431        return sql, params
 432
 433    def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
 434        """
 435        Return enough information to recreate the field as a 4-tuple:
 436
 437         * The name of the field on the model, if contribute_to_class() has
 438           been run.
 439         * The import path of the field, including the class, e.g.
 440           plain.models.IntegerField. This should be the most portable
 441           version, so less specific may be better.
 442         * A list of positional arguments.
 443         * A dict of keyword arguments.
 444
 445        Note that the positional or keyword arguments must contain values of
 446        the following types (including inner values of collection types):
 447
 448         * None, bool, str, int, float, complex, set, frozenset, list, tuple,
 449           dict
 450         * UUID
 451         * datetime.datetime (naive), datetime.date
 452         * top-level classes, top-level functions - will be referenced by their
 453           full import path
 454         * Storage instances - these have their own deconstruct() method
 455
 456        This is because the values here must be serialized into a text format
 457        (possibly new Python code, possibly JSON) and these are the only types
 458        with encoding handlers defined.
 459
 460        There's no need to return the exact way the field was instantiated this
 461        time, just ensure that the resulting field is the same - prefer keyword
 462        arguments over positional ones, and omit parameters with their default
 463        values.
 464        """
 465        # Short-form way of fetching all the default parameters
 466        keywords = {}
 467        possibles = {
 468            "max_length": None,
 469            "required": True,
 470            "allow_null": False,
 471            "default": NOT_PROVIDED,
 472            "choices": None,
 473            "db_column": None,
 474            "db_comment": None,
 475            "validators": [],
 476            "error_messages": None,
 477        }
 478        attr_overrides = {
 479            "error_messages": "_error_messages",
 480            "validators": "_validators",
 481        }
 482        equals_comparison = {"choices", "validators"}
 483        for name, default in possibles.items():
 484            value = getattr(self, attr_overrides.get(name, name))
 485            # Unroll anything iterable for choices into a concrete list
 486            if name == "choices" and isinstance(value, collections.abc.Iterable):
 487                value = list(value)
 488            # Do correct kind of comparison
 489            if name in equals_comparison:
 490                if value != default:
 491                    keywords[name] = value
 492            else:
 493                if value is not default:
 494                    keywords[name] = value
 495        # Work out path - we shorten it for known Plain core fields
 496        path = f"{self.__class__.__module__}.{self.__class__.__qualname__}"
 497        if path.startswith("plain.models.fields.related"):
 498            path = path.replace("plain.models.fields.related", "plain.models")
 499        elif path.startswith("plain.models.fields.json"):
 500            path = path.replace("plain.models.fields.json", "plain.models")
 501        elif path.startswith("plain.models.fields.proxy"):
 502            path = path.replace("plain.models.fields.proxy", "plain.models")
 503        elif path.startswith("plain.models.fields"):
 504            path = path.replace("plain.models.fields", "plain.models")
 505        # Return basic info - other fields should override this.
 506        # Note: self.name can be None during migration state rendering when fields are cloned
 507        return (self.name, path, [], keywords)
 508
 509    def clone(self) -> Self:
 510        """
 511        Uses deconstruct() to clone a new copy of this Field.
 512        Will not preserve any class attachments/attribute names.
 513        """
 514        name, path, args, kwargs = self.deconstruct()
 515        return cast(Self, self.__class__(*args, **kwargs))
 516
 517    def __eq__(self, other: object) -> bool:
 518        # Needed for @total_ordering
 519        if isinstance(other, Field):
 520            return self.creation_counter == other.creation_counter and getattr(
 521                self, "model", None
 522            ) == getattr(other, "model", None)
 523        return NotImplemented
 524
 525    def __lt__(self, other: object) -> bool:
 526        # This is needed because bisect does not take a comparison function.
 527        # Order by creation_counter first for backward compatibility.
 528        if not isinstance(other, Field):
 529            return NotImplemented
 530
 531        # Type narrowing: other is now known to be a Field
 532        other_field: Field[Any] = other
 533
 534        if (
 535            self.creation_counter != other_field.creation_counter
 536            or not hasattr(self, "model")
 537            and not hasattr(other_field, "model")
 538        ):
 539            return self.creation_counter < other_field.creation_counter
 540        elif hasattr(self, "model") != hasattr(other_field, "model"):
 541            return not hasattr(self, "model")  # Order no-model fields first
 542        else:
 543            # creation_counter's are equal, compare only models.
 544            # Use getattr with defaults to satisfy type checker
 545            self_pkg = getattr(getattr(self, "model", None), "model_options", None)
 546            other_pkg = getattr(
 547                getattr(other_field, "model", None), "model_options", None
 548            )
 549            if self_pkg is not None and other_pkg is not None:
 550                return (
 551                    self_pkg.package_label,
 552                    self_pkg.model_name,
 553                ) < (
 554                    other_pkg.package_label,
 555                    other_pkg.model_name,
 556                )
 557            # Fallback if model_options not available
 558            return self.creation_counter < other_field.creation_counter
 559
 560    def __hash__(self) -> int:
 561        return hash(self.creation_counter)
 562
 563    def __deepcopy__(self, memodict: dict[int, Any]) -> Self:
 564        # We don't have to deepcopy very much here, since most things are not
 565        # intended to be altered after initial creation.
 566        obj = copy.copy(self)
 567        memodict[id(self)] = obj
 568        return obj
 569
 570    def __copy__(self) -> Self:
 571        # We need to avoid hitting __reduce__, so define this
 572        # slightly weird copy construct.
 573        obj = Empty()
 574        obj.__class__ = self.__class__
 575        obj.__dict__ = self.__dict__.copy()
 576        return cast(Self, obj)
 577
 578    def __reduce__(
 579        self,
 580    ) -> (
 581        tuple[Callable[..., Any], tuple[Any, ...], dict[str, Any]]
 582        | tuple[Callable[..., Field[Any] | ForeignObjectRel], tuple[str, str, str]]
 583    ):
 584        """
 585        Pickling should return the model._model_meta.fields instance of the field,
 586        not a new copy of that field. So, use the app registry to load the
 587        model and then the field back.
 588        """
 589        model = getattr(self, "model", None)
 590        if model is None:
 591            # Fields are sometimes used without attaching them to models (for
 592            # example in aggregation). In this case give back a plain field
 593            # instance. The code below will create a new empty instance of
 594            # class self.__class__, then update its dict with self.__dict__
 595            # values - so, this is very close to normal pickle.
 596            state = self.__dict__.copy()
 597            # The _get_default cached_property can't be pickled due to lambda
 598            # usage.
 599            state.pop("_get_default", None)
 600            return _empty, (self.__class__,), state
 601        assert self.name is not None
 602        options = model.model_options
 603        return _load_field, (
 604            options.package_label,
 605            options.object_name,
 606            self.name,
 607        )
 608
 609    def get_id_value_on_save(self, instance: Any) -> Any:
 610        """
 611        Hook to generate new primary key values on save. This method is called when
 612        saving instances with no primary key value set. If this method returns
 613        something else than None, then the returned value is used when saving
 614        the new instance.
 615        """
 616        if self.default:
 617            return self.get_default()
 618        return None
 619
 620    def to_python(self, value: Any) -> Any:
 621        """
 622        Convert the input value into the expected Python data type, raising
 623        plain.exceptions.ValidationError if the data can't be converted.
 624        Return the converted value. Subclasses should override this.
 625        """
 626        return value
 627
 628    @cached_property
 629    def error_messages(self) -> dict[str, str]:
 630        messages = {}
 631        for c in reversed(self.__class__.__mro__):
 632            messages.update(getattr(c, "default_error_messages", {}))
 633        messages.update(self._error_messages or {})
 634        return messages
 635
 636    @cached_property
 637    def validators(self) -> list[Callable[..., Any]]:
 638        """
 639        Some validators can't be created at field initialization time.
 640        This method provides a way to delay their creation until required.
 641        """
 642        return [*self.default_validators, *self._validators]
 643
 644    def run_validators(self, value: Any) -> None:
 645        if value in self.empty_values:
 646            return
 647
 648        errors = []
 649        for v in self.validators:
 650            try:
 651                v(value)
 652            except exceptions.ValidationError as e:
 653                if hasattr(e, "code") and e.code in self.error_messages:
 654                    e.message = self.error_messages[e.code]
 655                errors.extend(e.error_list)
 656
 657        if errors:
 658            raise exceptions.ValidationError(errors)
 659
 660    def validate(self, value: Any, model_instance: Any) -> None:
 661        """
 662        Validate value and raise ValidationError if necessary. Subclasses
 663        should override this to provide validation logic.
 664        """
 665
 666        if self.choices is not None and value not in self.empty_values:
 667            for option_key, option_value in self.choices:
 668                if isinstance(option_value, list | tuple):
 669                    # This is an optgroup, so look inside the group for
 670                    # options.
 671                    for optgroup_key, optgroup_value in option_value:
 672                        if value == optgroup_key:
 673                            return
 674                elif value == option_key:
 675                    return
 676            raise exceptions.ValidationError(
 677                self.error_messages["invalid_choice"],
 678                code="invalid_choice",
 679                params={"value": value},
 680            )
 681
 682        if value is None and not self.allow_null:
 683            raise exceptions.ValidationError(
 684                self.error_messages["allow_null"], code="allow_null"
 685            )
 686
 687        if self.required and value in self.empty_values:
 688            raise exceptions.ValidationError(
 689                self.error_messages["required"], code="required"
 690            )
 691
 692    def clean(self, value: Any, model_instance: Any) -> Any:
 693        """
 694        Convert the value's type and run validation. Validation errors
 695        from to_python() and validate() are propagated. Return the correct
 696        value if no error is raised.
 697        """
 698        value = self.to_python(value)
 699        self.validate(value, model_instance)
 700        self.run_validators(value)
 701        return value
 702
 703    def db_type_parameters(self, connection: BaseDatabaseWrapper) -> DictWrapper:
 704        return DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
 705
 706    def db_check(self, connection: BaseDatabaseWrapper) -> str | None:
 707        """
 708        Return the database column check constraint for this field, for the
 709        provided connection. Works the same way as db_type() for the case that
 710        get_internal_type() does not map to a preexisting model field.
 711        """
 712        data = self.db_type_parameters(connection)
 713        try:
 714            return (
 715                connection.data_type_check_constraints[self.get_internal_type()] % data
 716            )
 717        except KeyError:
 718            return None
 719
 720    def db_type(self, connection: BaseDatabaseWrapper) -> str | None:
 721        """
 722        Return the database column data type for this field, for the provided
 723        connection.
 724        """
 725        # The default implementation of this method looks at the
 726        # backend-specific data_types dictionary, looking up the field by its
 727        # "internal type".
 728        #
 729        # A Field class can implement the get_internal_type() method to specify
 730        # which *preexisting* Plain Field class it's most similar to -- i.e.,
 731        # a custom field might be represented by a TEXT column type, which is
 732        # the same as the TextField Plain field type, which means the custom
 733        # field's get_internal_type() returns 'TextField'.
 734        #
 735        # But the limitation of the get_internal_type() / data_types approach
 736        # is that it cannot handle database column types that aren't already
 737        # mapped to one of the built-in Plain field types. In this case, you
 738        # can implement db_type() instead of get_internal_type() to specify
 739        # exactly which wacky database column type you want to use.
 740        data = self.db_type_parameters(connection)
 741        try:
 742            column_type = connection.data_types[self.get_internal_type()]
 743        except KeyError:
 744            return None
 745        else:
 746            # column_type is either a single-parameter function or a string.
 747            if callable(column_type):
 748                return column_type(data)
 749            return column_type % data
 750
 751    def rel_db_type(self, connection: BaseDatabaseWrapper) -> str | None:
 752        """
 753        Return the data type that a related field pointing to this field should
 754        use. For example, this method is called by ForeignKeyField to determine its data type.
 755        """
 756        return self.db_type(connection)
 757
 758    def cast_db_type(self, connection: BaseDatabaseWrapper) -> str | None:
 759        """Return the data type to use in the Cast() function."""
 760        db_type = connection.ops.cast_data_types.get(self.get_internal_type())
 761        if db_type:
 762            return db_type % self.db_type_parameters(connection)
 763        return self.db_type(connection)
 764
 765    def db_parameters(self, connection: BaseDatabaseWrapper) -> dict[str, Any]:
 766        """
 767        Extension of db_type(), providing a range of different return values
 768        (type, checks). This will look at db_type(), allowing custom model
 769        fields to override it.
 770        """
 771        type_string = self.db_type(connection)
 772        check_string = self.db_check(connection)
 773        return {
 774            "type": type_string,
 775            "check": check_string,
 776        }
 777
 778    def db_type_suffix(self, connection: BaseDatabaseWrapper) -> str | None:
 779        return connection.data_types_suffix.get(self.get_internal_type())
 780
 781    def get_db_converters(
 782        self, connection: BaseDatabaseWrapper
 783    ) -> list[Callable[..., Any]]:
 784        if from_db_value := getattr(self, "from_db_value", None):
 785            return [from_db_value]
 786        return []
 787
 788    @property
 789    def db_returning(self) -> bool:
 790        """
 791        Private API intended only to be used by Plain itself. Currently only
 792        the PostgreSQL backend supports returning multiple fields on a model.
 793        """
 794        return False
 795
 796    def set_attributes_from_name(self, name: str) -> None:
 797        self.name = self.name or name
 798        self.attname, self.column = self.get_attname_column()
 799        self.concrete = self.column is not None
 800
 801    def contribute_to_class(self, cls: type[Model], name: str) -> None:
 802        """
 803        Register the field with the model class it belongs to.
 804
 805        Field now acts as its own descriptor - it stays on the class and handles
 806        __get__/__set__/__delete__ directly.
 807        """
 808        self.set_attributes_from_name(name)
 809        self.model = cls
 810        cls._model_meta.add_field(self)
 811
 812        # Field is now a descriptor itself - ensure it's set on the class
 813        # This is important for inherited fields that get deepcopied in Meta.__get__
 814        if self.column:
 815            setattr(cls, self.attname, self)
 816
 817    # Descriptor protocol implementation
 818    @overload
 819    def __get__(self, instance: None, owner: type) -> Self: ...
 820
 821    @overload
 822    def __get__(self, instance: Any, owner: type) -> T: ...
 823
 824    def __get__(self, instance: Any | None, owner: type) -> Self | T:
 825        """
 826        Descriptor __get__ for attribute access.
 827
 828        Class access (User.email) returns the Field descriptor itself.
 829        Instance access (user.email) returns the field value from instance.__dict__,
 830        with lazy loading support if the value is not yet loaded.
 831        """
 832        # Class access - return the Field descriptor
 833        if instance is None:
 834            return self
 835
 836        # If field hasn't been contributed to a class yet (e.g., used standalone
 837        # as an output_field in aggregates), just return self
 838        if not hasattr(self, "attname"):
 839            return self
 840
 841        # Instance access - get value from instance dict
 842        data = instance.__dict__
 843        field_name = self.attname
 844
 845        # If value not in dict, lazy load from database
 846        if field_name not in data:
 847            # Deferred field - load it from the database
 848            instance.refresh_from_db(fields=[field_name])
 849
 850        return data.get(field_name)
 851
 852    def __set__(self, instance: Any, value: Any) -> None:
 853        """
 854        Descriptor __set__ for attribute assignment.
 855
 856        Validates and converts the value using to_python(), then stores it
 857        in instance.__dict__[attname].
 858        """
 859        # Safety check: ensure field has been properly initialized
 860        if not hasattr(self, "attname"):
 861            raise AttributeError(
 862                f"Field {self.__class__.__name__} has not been initialized properly. "
 863                f"The field's contribute_to_class() has not been called yet. "
 864                f"This usually means the field is being used before it was added to a model class."
 865            )
 866
 867        # Convert/validate the value
 868        if value is not None:
 869            value = self.to_python(value)
 870
 871        # Store in instance dict
 872        instance.__dict__[self.attname] = value
 873
 874    def __delete__(self, instance: Any) -> None:
 875        """
 876        Descriptor __delete__ for attribute deletion.
 877
 878        Removes the value from instance.__dict__.
 879        """
 880        try:
 881            del instance.__dict__[self.attname]
 882        except KeyError:
 883            raise AttributeError(
 884                f"{instance.__class__.__name__!r} object has no attribute {self.attname!r}"
 885            )
 886
 887    def get_attname(self) -> str:
 888        assert self.name is not None  # Field name must be set
 889        return self.name
 890
 891    def get_attname_column(self) -> tuple[str, str]:
 892        attname = self.get_attname()
 893        column = self.db_column or attname
 894        return attname, column
 895
 896    def get_internal_type(self) -> str:
 897        return self.__class__.__name__
 898
 899    def pre_save(self, model_instance: Any, add: bool) -> Any:
 900        """Return field's value just before saving."""
 901        return getattr(model_instance, self.attname)
 902
 903    def get_prep_value(self, value: Any) -> Any:
 904        """Perform preliminary non-db specific value checks and conversions."""
 905        if isinstance(value, Promise):
 906            value = value._proxy____cast()
 907        return value
 908
 909    def get_db_prep_value(
 910        self, value: Any, connection: BaseDatabaseWrapper, prepared: bool = False
 911    ) -> Any:
 912        """
 913        Return field's value prepared for interacting with the database backend.
 914
 915        Used by the default implementations of get_db_prep_save().
 916        """
 917        if not prepared:
 918            value = self.get_prep_value(value)
 919        return value
 920
 921    def get_db_prep_save(self, value: Any, connection: BaseDatabaseWrapper) -> Any:
 922        """Return field's value prepared for saving into a database."""
 923        if hasattr(value, "as_sql"):
 924            return value
 925        return self.get_db_prep_value(value, connection=connection, prepared=False)
 926
 927    def has_default(self) -> bool:
 928        """Return a boolean of whether this field has a default value."""
 929        return self.default is not NOT_PROVIDED
 930
 931    def get_default(self) -> Any:
 932        """Return the default value for this field."""
 933        return self._get_default()
 934
 935    @cached_property
 936    def _get_default(self) -> Callable[[], Any]:
 937        if self.has_default():
 938            if callable(self.default):
 939                return self.default
 940            return lambda: self.default
 941
 942        if not self.empty_strings_allowed or self.allow_null:
 943            return return_None
 944        return str  # return empty string
 945
 946    def get_limit_choices_to(self) -> Any:
 947        """
 948        Return ``limit_choices_to`` for this model field.
 949        Overridden by related fields (ForeignKey, etc.).
 950        """
 951        raise NotImplementedError(
 952            "get_limit_choices_to() should only be called on related fields"
 953        )
 954
 955    def get_choices(
 956        self,
 957        include_blank: bool = True,
 958        blank_choice: list[tuple[str, str]] = BLANK_CHOICE_DASH,
 959        limit_choices_to: Any = None,
 960        ordering: tuple[str, ...] = (),
 961    ) -> list[tuple[Any, str]]:
 962        """
 963        Return choices with a default blank choices included, for use
 964        as <select> choices for this field.
 965        """
 966        if self.choices is not None:
 967            choices = list(self.choices)
 968            if include_blank:
 969                blank_defined = any(
 970                    choice in ("", None) for choice, _ in self.flatchoices
 971                )
 972                if not blank_defined:
 973                    choices = blank_choice + choices
 974            return choices
 975        remote_field = getattr(self, "remote_field", None)
 976        if remote_field is None or getattr(remote_field, "model", None) is None:
 977            return blank_choice if include_blank else []
 978        rel_model = remote_field.model
 979        limit_choices_to = limit_choices_to or self.get_limit_choices_to()
 980        related_field_name = (
 981            remote_field.get_related_field().attname
 982            if hasattr(remote_field, "get_related_field")
 983            else "id"
 984        )
 985        choice_func = operator.attrgetter(related_field_name)
 986        qs = rel_model.query.complex_filter(limit_choices_to)
 987        if ordering:
 988            qs = qs.order_by(*ordering)
 989        return (blank_choice if include_blank else []) + [
 990            (choice_func(x), str(x)) for x in qs
 991        ]
 992
 993    def value_to_string(self, obj: Any) -> str:
 994        """
 995        Return a string value of this field from the passed obj.
 996        This is used by the serialization framework.
 997        """
 998        return str(self.value_from_object(obj))
 999
1000    def _get_flatchoices(self) -> list[tuple[Any, Any]]:
1001        """Flattened version of choices tuple."""
1002        if self.choices is None:
1003            return []
1004        flat = []
1005        for choice, value in self.choices:
1006            if isinstance(value, list | tuple):
1007                flat.extend(value)
1008            else:
1009                flat.append((choice, value))
1010        return flat
1011
1012    flatchoices = property(_get_flatchoices)
1013
1014    def save_form_data(self, instance: Any, data: Any) -> None:
1015        assert self.name is not None
1016        setattr(instance, self.name, data)
1017
1018    def value_from_object(self, obj: Any) -> Any:
1019        """Return the value of this field in the given model instance."""
1020        return getattr(obj, self.attname)
1021
1022
1023class BooleanField(Field[bool]):
1024    empty_strings_allowed = False
1025    default_error_messages = {
1026        "invalid": '"%(value)s" value must be either True or False.',
1027        "invalid_nullable": '"%(value)s" value must be either True, False, or None.',
1028    }
1029    description = "Boolean (Either True or False)"
1030
1031    def get_internal_type(self) -> str:
1032        return "BooleanField"
1033
1034    def to_python(self, value: Any) -> Any:
1035        if self.allow_null and value in self.empty_values:
1036            return None
1037        if value in (True, False):
1038            # 1/0 are equal to True/False. bool() converts former to latter.
1039            return bool(value)
1040        if value in ("t", "True", "1"):
1041            return True
1042        if value in ("f", "False", "0"):
1043            return False
1044        raise exceptions.ValidationError(
1045            self.error_messages["invalid_nullable" if self.allow_null else "invalid"],
1046            code="invalid",
1047            params={"value": value},
1048        )
1049
1050    def get_prep_value(self, value: Any) -> Any:
1051        value = super().get_prep_value(value)
1052        if value is None:
1053            return None
1054        return self.to_python(value)
1055
1056
1057class CharField(Field[str]):
1058    def __init__(self, *, db_collation: str | None = None, **kwargs: Any):
1059        super().__init__(**kwargs)
1060        self.db_collation = db_collation
1061        if self.max_length is not None:
1062            self.validators.append(validators.MaxLengthValidator(self.max_length))
1063
1064    @property
1065    def description(self) -> str:
1066        if self.max_length is not None:
1067            return "String (up to %(max_length)s)"
1068        else:
1069            return "String (unlimited)"
1070
1071    def preflight(self, **kwargs: Any) -> list[PreflightResult]:
1072        return [
1073            *super().preflight(**kwargs),
1074            *self._check_db_collation(),
1075            *self._check_max_length_attribute(),
1076        ]
1077
1078    def _check_max_length_attribute(self, **kwargs: Any) -> list[PreflightResult]:
1079        if self.max_length is None:
1080            if (
1081                db_connection.features.supports_unlimited_charfield
1082                or "supports_unlimited_charfield"
1083                in self.model.model_options.required_db_features
1084            ):
1085                return []
1086            return [
1087                PreflightResult(
1088                    fix="CharFields must define a 'max_length' attribute.",
1089                    obj=self,
1090                    id="fields.charfield_missing_max_length",
1091                )
1092            ]
1093        elif (
1094            not isinstance(self.max_length, int)
1095            or isinstance(self.max_length, bool)
1096            or self.max_length <= 0
1097        ):
1098            return [
1099                PreflightResult(
1100                    fix="'max_length' must be a positive integer.",
1101                    obj=self,
1102                    id="fields.charfield_invalid_max_length",
1103                )
1104            ]
1105        else:
1106            return []
1107
1108    def _check_db_collation(self) -> list[PreflightResult]:
1109        errors = []
1110        if not (
1111            self.db_collation is None
1112            or "supports_collation_on_charfield"
1113            in self.model.model_options.required_db_features
1114            or db_connection.features.supports_collation_on_charfield
1115        ):
1116            errors.append(
1117                PreflightResult(
1118                    fix=f"{db_connection.display_name} does not support a database collation on "
1119                    "CharFields.",
1120                    obj=self,
1121                    id="fields.db_collation_unsupported",
1122                ),
1123            )
1124        return errors
1125
1126    def cast_db_type(self, connection: BaseDatabaseWrapper) -> str | None:
1127        if self.max_length is None:
1128            return connection.ops.cast_char_field_without_max_length
1129        return super().cast_db_type(connection)
1130
1131    def db_parameters(self, connection: BaseDatabaseWrapper) -> dict[str, Any]:
1132        db_params = super().db_parameters(connection)
1133        db_params["collation"] = self.db_collation
1134        return db_params
1135
1136    def get_internal_type(self) -> str:
1137        return "CharField"
1138
1139    def to_python(self, value: Any) -> Any:
1140        if isinstance(value, str) or value is None:
1141            return value
1142        return str(value)
1143
1144    def get_prep_value(self, value: Any) -> Any:
1145        value = super().get_prep_value(value)
1146        return self.to_python(value)
1147
1148    def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
1149        name, path, args, kwargs = super().deconstruct()
1150        if self.db_collation:
1151            kwargs["db_collation"] = self.db_collation
1152        return name, path, args, kwargs
1153
1154
1155def _to_naive(value: datetime.datetime) -> datetime.datetime:
1156    if timezone.is_aware(value):
1157        value = timezone.make_naive(value, datetime.UTC)
1158    return value
1159
1160
1161def _get_naive_now() -> datetime.datetime:
1162    return _to_naive(timezone.now())
1163
1164
1165class DateTimeCheckMixin(Field):
1166    auto_now: bool
1167    auto_now_add: bool
1168
1169    def preflight(self, **kwargs: Any) -> list[PreflightResult]:
1170        return [
1171            *super().preflight(**kwargs),
1172            *self._check_mutually_exclusive_options(),
1173            *self._check_fix_default_value(),
1174        ]
1175
1176    def _check_mutually_exclusive_options(self) -> list[PreflightResult]:
1177        # auto_now, auto_now_add, and default are mutually exclusive
1178        # options. The use of more than one of these options together
1179        # will trigger an Error
1180        mutually_exclusive_options = [
1181            self.auto_now_add,
1182            self.auto_now,
1183            self.has_default(),
1184        ]
1185        enabled_options = [
1186            option not in (None, False) for option in mutually_exclusive_options
1187        ].count(True)
1188        if enabled_options > 1:
1189            return [
1190                PreflightResult(
1191                    fix="The options auto_now, auto_now_add, and default "
1192                    "are mutually exclusive. Only one of these options "
1193                    "may be present.",
1194                    obj=self,
1195                    id="fields.datetime_auto_options_mutually_exclusive",
1196                )
1197            ]
1198        else:
1199            return []
1200
1201    def _check_fix_default_value(self) -> list[PreflightResult]:
1202        return []
1203
1204    # Concrete subclasses use this in their implementations of
1205    # _check_fix_default_value().
1206    def _check_if_value_fixed(
1207        self,
1208        value: datetime.date | datetime.datetime,
1209        now: datetime.datetime | None = None,
1210    ) -> list[PreflightResult]:
1211        """
1212        Check if the given value appears to have been provided as a "fixed"
1213        time value, and include a warning in the returned list if it does. The
1214        value argument must be a date object or aware/naive datetime object. If
1215        now is provided, it must be a naive datetime object.
1216        """
1217        if now is None:
1218            now = _get_naive_now()
1219        offset = datetime.timedelta(seconds=10)
1220        lower = now - offset
1221        upper = now + offset
1222        if isinstance(value, datetime.datetime):
1223            value = _to_naive(value)
1224        else:
1225            assert isinstance(value, datetime.date)
1226            lower = lower.date()
1227            upper = upper.date()
1228        if lower <= value <= upper:
1229            return [
1230                PreflightResult(
1231                    fix="Fixed default value provided. "
1232                    "It seems you set a fixed date / time / datetime "
1233                    "value as default for this field. This may not be "
1234                    "what you want. If you want to have the current date "
1235                    "as default, use `plain.utils.timezone.now`",
1236                    obj=self,
1237                    id="fields.datetime_naive_default_value",
1238                    warning=True,
1239                )
1240            ]
1241        return []
1242
1243
1244class DateField(DateTimeCheckMixin, Field[datetime.date]):
1245    empty_strings_allowed = False
1246    default_error_messages = {
1247        "invalid": '"%(value)s" value has an invalid date format. It must be in YYYY-MM-DD format.',
1248        "invalid_date": '"%(value)s" value has the correct format (YYYY-MM-DD) but it is an invalid date.',
1249    }
1250    description = "Date (without time)"
1251
1252    def __init__(
1253        self, *, auto_now: bool = False, auto_now_add: bool = False, **kwargs: Any
1254    ):
1255        self.auto_now, self.auto_now_add = auto_now, auto_now_add
1256        if auto_now or auto_now_add:
1257            kwargs["required"] = False
1258        super().__init__(**kwargs)
1259
1260    def _check_fix_default_value(self) -> list[PreflightResult]:
1261        """
1262        Warn that using an actual date or datetime value is probably wrong;
1263        it's only evaluated on server startup.
1264        """
1265        if not self.has_default():
1266            return []
1267
1268        value = self.default
1269        if isinstance(value, datetime.datetime):
1270            value = _to_naive(value).date()
1271        elif isinstance(value, datetime.date):
1272            pass
1273        else:
1274            # No explicit date / datetime value -- no checks necessary
1275            return []
1276        # At this point, value is a date object.
1277        return self._check_if_value_fixed(value)
1278
1279    def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
1280        name, path, args, kwargs = super().deconstruct()
1281        if self.auto_now:
1282            kwargs["auto_now"] = True
1283        if self.auto_now_add:
1284            kwargs["auto_now_add"] = True
1285        if self.auto_now or self.auto_now_add:
1286            del kwargs["required"]
1287        return name, path, args, kwargs
1288
1289    def get_internal_type(self) -> str:
1290        return "DateField"
1291
1292    def to_python(self, value: Any) -> Any:
1293        if value is None:
1294            return value
1295        if isinstance(value, datetime.datetime):
1296            if timezone.is_aware(value):
1297                # Convert aware datetimes to the default time zone
1298                # before casting them to dates (#17742).
1299                default_timezone = timezone.get_default_timezone()
1300                value = timezone.make_naive(value, default_timezone)
1301            return value.date()
1302        if isinstance(value, datetime.date):
1303            return value
1304
1305        try:
1306            parsed = parse_date(value)
1307            if parsed is not None:
1308                return parsed
1309        except ValueError:
1310            raise exceptions.ValidationError(
1311                self.error_messages["invalid_date"],
1312                code="invalid_date",
1313                params={"value": value},
1314            )
1315
1316        raise exceptions.ValidationError(
1317            self.error_messages["invalid"],
1318            code="invalid",
1319            params={"value": value},
1320        )
1321
1322    def pre_save(self, model_instance: Any, add: bool) -> Any:
1323        if self.auto_now or (self.auto_now_add and add):
1324            value = datetime.date.today()
1325            setattr(model_instance, self.attname, value)
1326            return value
1327        else:
1328            return super().pre_save(model_instance, add)
1329
1330    def get_prep_value(self, value: Any) -> Any:
1331        value = super().get_prep_value(value)
1332        return self.to_python(value)
1333
1334    def get_db_prep_value(
1335        self, value: Any, connection: BaseDatabaseWrapper, prepared: bool = False
1336    ) -> Any:
1337        # Casts dates into the format expected by the backend
1338        if not prepared:
1339            value = self.get_prep_value(value)
1340        return connection.ops.adapt_datefield_value(value)
1341
1342    def value_to_string(self, obj: Any) -> str:
1343        val = self.value_from_object(obj)
1344        return "" if val is None else val.isoformat()
1345
1346
1347class DateTimeField(DateField):
1348    empty_strings_allowed = False
1349    default_error_messages = {
1350        "invalid": '"%(value)s" value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.',
1351        "invalid_date": '"%(value)s" value has the correct format (YYYY-MM-DD) but it is an invalid date.',
1352        "invalid_datetime": '"%(value)s" value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) but it is an invalid date/time.',
1353    }
1354    description = "Date (with time)"
1355
1356    # __init__ is inherited from DateField
1357
1358    def _check_fix_default_value(self) -> list[PreflightResult]:
1359        """
1360        Warn that using an actual date or datetime value is probably wrong;
1361        it's only evaluated on server startup.
1362        """
1363        if not self.has_default():
1364            return []
1365
1366        value = self.default
1367        if isinstance(value, datetime.datetime | datetime.date):
1368            return self._check_if_value_fixed(value)
1369        # No explicit date / datetime value -- no checks necessary.
1370        return []
1371
1372    def get_internal_type(self) -> str:
1373        return "DateTimeField"
1374
1375    def to_python(self, value: Any) -> Any:
1376        if value is None:
1377            return value
1378        if isinstance(value, datetime.datetime):
1379            return value
1380        if isinstance(value, datetime.date):
1381            value = datetime.datetime(value.year, value.month, value.day)
1382
1383            # For backwards compatibility, interpret naive datetimes in
1384            # local time. This won't work during DST change, but we can't
1385            # do much about it, so we let the exceptions percolate up the
1386            # call stack.
1387            warnings.warn(
1388                f"DateTimeField {self.model.__name__}.{self.name} received a naive datetime "
1389                f"({value}) while time zone support is active.",
1390                RuntimeWarning,
1391            )
1392            default_timezone = timezone.get_default_timezone()
1393            value = timezone.make_aware(value, default_timezone)
1394
1395            return value
1396
1397        try:
1398            parsed = parse_datetime(value)
1399            if parsed is not None:
1400                return parsed
1401        except ValueError:
1402            raise exceptions.ValidationError(
1403                self.error_messages["invalid_datetime"],
1404                code="invalid_datetime",
1405                params={"value": value},
1406            )
1407
1408        try:
1409            parsed = parse_date(value)
1410            if parsed is not None:
1411                return datetime.datetime(parsed.year, parsed.month, parsed.day)
1412        except ValueError:
1413            raise exceptions.ValidationError(
1414                self.error_messages["invalid_date"],
1415                code="invalid_date",
1416                params={"value": value},
1417            )
1418
1419        raise exceptions.ValidationError(
1420            self.error_messages["invalid"],
1421            code="invalid",
1422            params={"value": value},
1423        )
1424
1425    def pre_save(self, model_instance: Any, add: bool) -> Any:
1426        if self.auto_now or (self.auto_now_add and add):
1427            value = timezone.now()
1428            setattr(model_instance, self.attname, value)
1429            return value
1430        else:
1431            return super().pre_save(model_instance, add)
1432
1433    def get_prep_value(self, value: Any) -> Any:
1434        value = super().get_prep_value(value)
1435        value = self.to_python(value)
1436        if value is not None and timezone.is_naive(value):
1437            # For backwards compatibility, interpret naive datetimes in local
1438            # time. This won't work during DST change, but we can't do much
1439            # about it, so we let the exceptions percolate up the call stack.
1440            try:
1441                name = f"{self.model.__name__}.{self.name}"
1442            except AttributeError:
1443                name = "(unbound)"
1444            warnings.warn(
1445                f"DateTimeField {name} received a naive datetime ({value})"
1446                " while time zone support is active.",
1447                RuntimeWarning,
1448            )
1449            default_timezone = timezone.get_default_timezone()
1450            value = timezone.make_aware(value, default_timezone)
1451        return value
1452
1453    def get_db_prep_value(
1454        self, value: Any, connection: BaseDatabaseWrapper, prepared: bool = False
1455    ) -> Any:
1456        # Casts datetimes into the format expected by the backend
1457        if not prepared:
1458            value = self.get_prep_value(value)
1459        return connection.ops.adapt_datetimefield_value(value)
1460
1461    def value_to_string(self, obj: Any) -> str:
1462        val = self.value_from_object(obj)
1463        return "" if val is None else val.isoformat()
1464
1465
1466class DecimalField(Field[decimal.Decimal]):
1467    empty_strings_allowed = False
1468    default_error_messages = {
1469        "invalid": '"%(value)s" value must be a decimal number.',
1470    }
1471    description = "Decimal number"
1472
1473    def __init__(
1474        self,
1475        *,
1476        max_digits: int | None = None,
1477        decimal_places: int | None = None,
1478        **kwargs: Any,
1479    ):
1480        self.max_digits, self.decimal_places = max_digits, decimal_places
1481        super().__init__(**kwargs)
1482
1483    def preflight(self, **kwargs: Any) -> list[PreflightResult]:
1484        errors = super().preflight(**kwargs)
1485
1486        digits_errors = [
1487            *self._check_decimal_places(),
1488            *self._check_max_digits(),
1489        ]
1490        if not digits_errors:
1491            errors.extend(self._check_decimal_places_and_max_digits())
1492        else:
1493            errors.extend(digits_errors)
1494        return errors
1495
1496    def _check_decimal_places(self) -> list[PreflightResult]:
1497        try:
1498            decimal_places = int(self.decimal_places)
1499            if decimal_places < 0:
1500                raise ValueError()
1501        except TypeError:
1502            return [
1503                PreflightResult(
1504                    fix="DecimalFields must define a 'decimal_places' attribute.",
1505                    obj=self,
1506                    id="fields.decimalfield_missing_decimal_places",
1507                )
1508            ]
1509        except ValueError:
1510            return [
1511                PreflightResult(
1512                    fix="'decimal_places' must be a non-negative integer.",
1513                    obj=self,
1514                    id="fields.decimalfield_invalid_decimal_places",
1515                )
1516            ]
1517        else:
1518            return []
1519
1520    def _check_max_digits(self) -> list[PreflightResult]:
1521        try:
1522            max_digits = int(self.max_digits)
1523            if max_digits <= 0:
1524                raise ValueError()
1525        except TypeError:
1526            return [
1527                PreflightResult(
1528                    fix="DecimalFields must define a 'max_digits' attribute.",
1529                    obj=self,
1530                    id="fields.decimalfield_missing_max_digits",
1531                )
1532            ]
1533        except ValueError:
1534            return [
1535                PreflightResult(
1536                    fix="'max_digits' must be a positive integer.",
1537                    obj=self,
1538                    id="fields.decimalfield_invalid_max_digits",
1539                )
1540            ]
1541        else:
1542            return []
1543
1544    def _check_decimal_places_and_max_digits(self) -> list[PreflightResult]:
1545        if int(self.decimal_places) > int(self.max_digits):
1546            return [
1547                PreflightResult(
1548                    fix="'max_digits' must be greater or equal to 'decimal_places'.",
1549                    obj=self,
1550                    id="fields.decimalfield_decimal_places_exceeds_max_digits",
1551                )
1552            ]
1553        return []
1554
1555    @cached_property
1556    def validators(self) -> list[Callable[..., Any]]:
1557        return super().validators + [
1558            validators.DecimalValidator(self.max_digits, self.decimal_places)
1559        ]
1560
1561    @cached_property
1562    def context(self) -> decimal.Context:
1563        return decimal.Context(prec=self.max_digits)
1564
1565    def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
1566        name, path, args, kwargs = super().deconstruct()
1567        if self.max_digits is not None:
1568            kwargs["max_digits"] = self.max_digits
1569        if self.decimal_places is not None:
1570            kwargs["decimal_places"] = self.decimal_places
1571        return name, path, args, kwargs
1572
1573    def get_internal_type(self) -> str:
1574        return "DecimalField"
1575
1576    def to_python(self, value: Any) -> Any:
1577        if value is None:
1578            return value
1579        try:
1580            if isinstance(value, float):
1581                decimal_value = self.context.create_decimal_from_float(value)
1582            else:
1583                decimal_value = decimal.Decimal(value)
1584        except (decimal.InvalidOperation, TypeError, ValueError):
1585            raise exceptions.ValidationError(
1586                self.error_messages["invalid"],
1587                code="invalid",
1588                params={"value": value},
1589            )
1590        if not decimal_value.is_finite():
1591            raise exceptions.ValidationError(
1592                self.error_messages["invalid"],
1593                code="invalid",
1594                params={"value": value},
1595            )
1596        return decimal_value
1597
1598    def get_db_prep_value(
1599        self, value: Any, connection: BaseDatabaseWrapper, prepared: bool = False
1600    ) -> Any:
1601        if not prepared:
1602            value = self.get_prep_value(value)
1603        if hasattr(value, "as_sql"):
1604            return value
1605        return connection.ops.adapt_decimalfield_value(
1606            value, self.max_digits, self.decimal_places
1607        )
1608
1609    def get_prep_value(self, value: Any) -> Any:
1610        value = super().get_prep_value(value)
1611        return self.to_python(value)
1612
1613
1614class DurationField(Field[datetime.timedelta]):
1615    """
1616    Store timedelta objects.
1617
1618    Use interval on PostgreSQL, INTERVAL DAY TO SECOND on Oracle, and bigint
1619    of microseconds on other databases.
1620    """
1621
1622    empty_strings_allowed = False
1623    default_error_messages = {
1624        "invalid": '"%(value)s" value has an invalid format. It must be in [DD] [[HH:]MM:]ss[.uuuuuu] format.',
1625    }
1626    description = "Duration"
1627
1628    def get_internal_type(self) -> str:
1629        return "DurationField"
1630
1631    def to_python(self, value: Any) -> Any:
1632        if value is None:
1633            return value
1634        if isinstance(value, datetime.timedelta):
1635            return value
1636        try:
1637            parsed = parse_duration(value)
1638        except ValueError:
1639            pass
1640        else:
1641            if parsed is not None:
1642                return parsed
1643
1644        raise exceptions.ValidationError(
1645            self.error_messages["invalid"],
1646            code="invalid",
1647            params={"value": value},
1648        )
1649
1650    def get_db_prep_value(
1651        self, value: Any, connection: BaseDatabaseWrapper, prepared: bool = False
1652    ) -> Any:
1653        if connection.features.has_native_duration_field:
1654            return value
1655        if value is None:
1656            return None
1657        return duration_microseconds(value)
1658
1659    def get_db_converters(
1660        self, connection: BaseDatabaseWrapper
1661    ) -> list[Callable[..., Any]]:
1662        converters = []
1663        if not connection.features.has_native_duration_field:
1664            converters.append(connection.ops.convert_durationfield_value)
1665        return converters + super().get_db_converters(connection)
1666
1667    def value_to_string(self, obj: Any) -> str:
1668        val = self.value_from_object(obj)
1669        return "" if val is None else duration_string(val)
1670
1671
1672class EmailField(CharField):
1673    default_validators = [validators.validate_email]
1674    description = "Email address"
1675
1676    def __init__(self, **kwargs: Any):
1677        # max_length=254 to be compliant with RFCs 3696 and 5321
1678        kwargs.setdefault("max_length", 254)
1679        super().__init__(**kwargs)
1680
1681    def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
1682        name, path, args, kwargs = super().deconstruct()
1683        # We do not exclude max_length if it matches default as we want to change
1684        # the default in future.
1685        return name, path, args, kwargs
1686
1687
1688class FloatField(Field[float]):
1689    empty_strings_allowed = False
1690    default_error_messages = {
1691        "invalid": '"%(value)s" value must be a float.',
1692    }
1693    description = "Floating point number"
1694
1695    def get_prep_value(self, value: Any) -> Any:
1696        value = super().get_prep_value(value)
1697        if value is None:
1698            return None
1699        try:
1700            return float(value)
1701        except (TypeError, ValueError) as e:
1702            raise e.__class__(
1703                f"Field '{self.name}' expected a number but got {value!r}.",
1704            ) from e
1705
1706    def get_internal_type(self) -> str:
1707        return "FloatField"
1708
1709    def to_python(self, value: Any) -> Any:
1710        if value is None:
1711            return value
1712        try:
1713            return float(value)
1714        except (TypeError, ValueError):
1715            raise exceptions.ValidationError(
1716                self.error_messages["invalid"],
1717                code="invalid",
1718                params={"value": value},
1719            )
1720
1721
1722class IntegerField(Field[int]):
1723    empty_strings_allowed = False
1724    default_error_messages = {
1725        "invalid": '"%(value)s" value must be an integer.',
1726    }
1727    description = "Integer"
1728
1729    def preflight(self, **kwargs: Any) -> list[PreflightResult]:
1730        return [
1731            *super().preflight(**kwargs),
1732            *self._check_max_length_warning(),
1733        ]
1734
1735    def _check_max_length_warning(self) -> list[PreflightResult]:
1736        if self.max_length is not None:
1737            return [
1738                PreflightResult(
1739                    fix=f"'max_length' is ignored when used with {self.__class__.__name__}. Remove 'max_length' from field.",
1740                    obj=self,
1741                    id="fields.max_length_ignored",
1742                    warning=True,
1743                )
1744            ]
1745        return []
1746
1747    @cached_property
1748    def validators(self) -> list[Callable[..., Any]]:
1749        # These validators can't be added at field initialization time since
1750        # they're based on values retrieved from the database connection.
1751        validators_ = super().validators
1752        internal_type = self.get_internal_type()
1753        min_value, max_value = db_connection.ops.integer_field_range(internal_type)
1754        if min_value is not None and not any(
1755            (
1756                isinstance(validator, validators.MinValueValidator)
1757                and (
1758                    validator.limit_value()
1759                    if callable(validator.limit_value)
1760                    else validator.limit_value
1761                )
1762                >= min_value
1763            )
1764            for validator in validators_
1765        ):
1766            validators_.append(validators.MinValueValidator(min_value))
1767        if max_value is not None and not any(
1768            (
1769                isinstance(validator, validators.MaxValueValidator)
1770                and (
1771                    validator.limit_value()
1772                    if callable(validator.limit_value)
1773                    else validator.limit_value
1774                )
1775                <= max_value
1776            )
1777            for validator in validators_
1778        ):
1779            validators_.append(validators.MaxValueValidator(max_value))
1780        return validators_
1781
1782    def get_prep_value(self, value: Any) -> Any:
1783        value = super().get_prep_value(value)
1784        if value is None:
1785            return None
1786        try:
1787            return int(value)
1788        except (TypeError, ValueError) as e:
1789            raise e.__class__(
1790                f"Field '{self.name}' expected a number but got {value!r}.",
1791            ) from e
1792
1793    def get_db_prep_value(
1794        self, value: Any, connection: BaseDatabaseWrapper, prepared: bool = False
1795    ) -> Any:
1796        value = super().get_db_prep_value(value, connection, prepared)
1797        return connection.ops.adapt_integerfield_value(value, self.get_internal_type())
1798
1799    def get_internal_type(self) -> str:
1800        return "IntegerField"
1801
1802    def to_python(self, value: Any) -> Any:
1803        if value is None:
1804            return value
1805        try:
1806            return int(value)
1807        except (TypeError, ValueError):
1808            raise exceptions.ValidationError(
1809                self.error_messages["invalid"],
1810                code="invalid",
1811                params={"value": value},
1812            )
1813
1814
1815class BigIntegerField(IntegerField):
1816    description = "Big (8 byte) integer"
1817
1818    def get_internal_type(self) -> str:
1819        return "BigIntegerField"
1820
1821
1822class SmallIntegerField(IntegerField):
1823    description = "Small integer"
1824
1825    def get_internal_type(self) -> str:
1826        return "SmallIntegerField"
1827
1828
1829class GenericIPAddressField(Field[str]):
1830    empty_strings_allowed = False
1831    description = "IP address"
1832    default_error_messages = {}
1833
1834    def __init__(
1835        self,
1836        *,
1837        protocol: str = "both",
1838        unpack_ipv4: bool = False,
1839        **kwargs: Any,
1840    ):
1841        self.unpack_ipv4 = unpack_ipv4
1842        self.protocol = protocol
1843        (
1844            self.default_validators,
1845            invalid_error_message,
1846        ) = validators.ip_address_validators(protocol, unpack_ipv4)
1847        self.default_error_messages["invalid"] = invalid_error_message
1848        kwargs["max_length"] = 39
1849        super().__init__(**kwargs)
1850
1851    def preflight(self, **kwargs: Any) -> list[PreflightResult]:
1852        return [
1853            *super().preflight(**kwargs),
1854            *self._check_required_and_null_values(),
1855        ]
1856
1857    def _check_required_and_null_values(self) -> list[PreflightResult]:
1858        if not getattr(self, "allow_null", False) and not getattr(
1859            self, "required", True
1860        ):
1861            return [
1862                PreflightResult(
1863                    fix="GenericIPAddressFields cannot have required=False if allow_null=False, "
1864                    "as blank values are stored as nulls.",
1865                    obj=self,
1866                    id="fields.generic_ip_field_null_blank_config",
1867                )
1868            ]
1869        return []
1870
1871    def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
1872        name, path, args, kwargs = super().deconstruct()
1873        if self.unpack_ipv4 is not False:
1874            kwargs["unpack_ipv4"] = self.unpack_ipv4
1875        if self.protocol != "both":
1876            kwargs["protocol"] = self.protocol
1877        if kwargs.get("max_length") == 39:
1878            del kwargs["max_length"]
1879        return name, path, args, kwargs
1880
1881    def get_internal_type(self) -> str:
1882        return "GenericIPAddressField"
1883
1884    def to_python(self, value: Any) -> Any:
1885        if value is None:
1886            return None
1887        if not isinstance(value, str):
1888            value = str(value)
1889        value = value.strip()
1890        if ":" in value:
1891            return clean_ipv6_address(
1892                value, self.unpack_ipv4, self.error_messages["invalid"]
1893            )
1894        return value
1895
1896    def get_db_prep_value(
1897        self, value: Any, connection: BaseDatabaseWrapper, prepared: bool = False
1898    ) -> Any:
1899        if not prepared:
1900            value = self.get_prep_value(value)
1901        return connection.ops.adapt_ipaddressfield_value(value)
1902
1903    def get_prep_value(self, value: Any) -> Any:
1904        value = super().get_prep_value(value)
1905        if value is None:
1906            return None
1907        if value and ":" in value:
1908            try:
1909                return clean_ipv6_address(value, self.unpack_ipv4)
1910            except exceptions.ValidationError:
1911                pass
1912        return str(value)
1913
1914
1915class _HasDbType(Protocol):
1916    """Protocol for objects that have a db_type method and integer_field_class."""
1917
1918    integer_field_class: type[IntegerField]
1919
1920    def db_type(self, connection: BaseDatabaseWrapper) -> str | None: ...
1921
1922
1923class PositiveIntegerRelDbTypeMixin(IntegerField):
1924    integer_field_class: type[IntegerField]
1925
1926    def __init_subclass__(cls, **kwargs: Any) -> None:
1927        super().__init_subclass__(**kwargs)
1928        if not hasattr(cls, "integer_field_class"):
1929            cls.integer_field_class = next(
1930                (
1931                    parent
1932                    for parent in cls.__mro__[1:]
1933                    if issubclass(parent, IntegerField)
1934                ),
1935                None,
1936            )
1937
1938    def rel_db_type(self: _HasDbType, connection: BaseDatabaseWrapper) -> str | None:
1939        """
1940        Return the data type that a related field pointing to this field should
1941        use. In most cases, a foreign key pointing to a positive integer
1942        primary key will have an integer column data type but some databases
1943        (e.g. MySQL) have an unsigned integer type. In that case
1944        (related_fields_match_type=True), the primary key should return its
1945        db_type.
1946        """
1947        if connection.features.related_fields_match_type:
1948            return self.db_type(connection)
1949        else:
1950            return self.integer_field_class().db_type(connection=connection)
1951
1952
1953class PositiveBigIntegerField(PositiveIntegerRelDbTypeMixin, BigIntegerField):
1954    description = "Positive big integer"
1955
1956    def get_internal_type(self) -> str:
1957        return "PositiveBigIntegerField"
1958
1959
1960class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):
1961    description = "Positive integer"
1962
1963    def get_internal_type(self) -> str:
1964        return "PositiveIntegerField"
1965
1966
1967class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, SmallIntegerField):
1968    description = "Positive small integer"
1969
1970    def get_internal_type(self) -> str:
1971        return "PositiveSmallIntegerField"
1972
1973
1974class TextField(Field[str]):
1975    description = "Text"
1976
1977    def __init__(self, *, db_collation: str | None = None, **kwargs: Any):
1978        super().__init__(**kwargs)
1979        self.db_collation = db_collation
1980
1981    def preflight(self, **kwargs: Any) -> list[PreflightResult]:
1982        return [
1983            *super().preflight(**kwargs),
1984            *self._check_db_collation(),
1985        ]
1986
1987    def _check_db_collation(self) -> list[PreflightResult]:
1988        errors = []
1989        if not (
1990            self.db_collation is None
1991            or "supports_collation_on_textfield"
1992            in self.model.model_options.required_db_features
1993            or db_connection.features.supports_collation_on_textfield
1994        ):
1995            errors.append(
1996                PreflightResult(
1997                    fix=f"{db_connection.display_name} does not support a database collation on "
1998                    "TextFields.",
1999                    obj=self,
2000                    id="fields.db_collation_unsupported",
2001                ),
2002            )
2003        return errors
2004
2005    def db_parameters(self, connection: BaseDatabaseWrapper) -> dict[str, Any]:
2006        db_params = super().db_parameters(connection)
2007        db_params["collation"] = self.db_collation
2008        return db_params
2009
2010    def get_internal_type(self) -> str:
2011        return "TextField"
2012
2013    def to_python(self, value: Any) -> Any:
2014        if isinstance(value, str) or value is None:
2015            return value
2016        return str(value)
2017
2018    def get_prep_value(self, value: Any) -> Any:
2019        value = super().get_prep_value(value)
2020        return self.to_python(value)
2021
2022    def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
2023        name, path, args, kwargs = super().deconstruct()
2024        if self.db_collation:
2025            kwargs["db_collation"] = self.db_collation
2026        return name, path, args, kwargs
2027
2028
2029class TimeField(DateTimeCheckMixin, Field[datetime.time]):
2030    empty_strings_allowed = False
2031    default_error_messages = {
2032        "invalid": '"%(value)s" value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] format.',
2033        "invalid_time": '"%(value)s" value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an invalid time.',
2034    }
2035    description = "Time"
2036
2037    def __init__(
2038        self, *, auto_now: bool = False, auto_now_add: bool = False, **kwargs: Any
2039    ):
2040        self.auto_now, self.auto_now_add = auto_now, auto_now_add
2041        if auto_now or auto_now_add:
2042            kwargs["required"] = False
2043        super().__init__(**kwargs)
2044
2045    def _check_fix_default_value(self) -> list[PreflightResult]:
2046        """
2047        Warn that using an actual date or datetime value is probably wrong;
2048        it's only evaluated on server startup.
2049        """
2050        if not self.has_default():
2051            return []
2052
2053        value = self.default
2054        if isinstance(value, datetime.datetime):
2055            now = None
2056        elif isinstance(value, datetime.time):
2057            now = _get_naive_now()
2058            # This will not use the right date in the race condition where now
2059            # is just before the date change and value is just past 0:00.
2060            value = datetime.datetime.combine(now.date(), value)
2061        else:
2062            # No explicit time / datetime value -- no checks necessary
2063            return []
2064        # At this point, value is a datetime object.
2065        return self._check_if_value_fixed(value, now=now)
2066
2067    def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
2068        name, path, args, kwargs = super().deconstruct()
2069        if self.auto_now is not False:
2070            kwargs["auto_now"] = self.auto_now
2071        if self.auto_now_add is not False:
2072            kwargs["auto_now_add"] = self.auto_now_add
2073        if self.auto_now or self.auto_now_add:
2074            del kwargs["required"]
2075        return name, path, args, kwargs
2076
2077    def get_internal_type(self) -> str:
2078        return "TimeField"
2079
2080    def to_python(self, value: Any) -> Any:
2081        if value is None:
2082            return None
2083        if isinstance(value, datetime.time):
2084            return value
2085        if isinstance(value, datetime.datetime):
2086            # Not usually a good idea to pass in a datetime here (it loses
2087            # information), but this can be a side-effect of interacting with a
2088            # database backend (e.g. Oracle), so we'll be accommodating.
2089            return value.time()
2090
2091        try:
2092            parsed = parse_time(value)
2093            if parsed is not None:
2094                return parsed
2095        except ValueError:
2096            raise exceptions.ValidationError(
2097                self.error_messages["invalid_time"],
2098                code="invalid_time",
2099                params={"value": value},
2100            )
2101
2102        raise exceptions.ValidationError(
2103            self.error_messages["invalid"],
2104            code="invalid",
2105            params={"value": value},
2106        )
2107
2108    def pre_save(self, model_instance: Any, add: bool) -> Any:
2109        if self.auto_now or (self.auto_now_add and add):
2110            value = datetime.datetime.now().time()
2111            setattr(model_instance, self.attname, value)
2112            return value
2113        else:
2114            return super().pre_save(model_instance, add)
2115
2116    def get_prep_value(self, value: Any) -> Any:
2117        value = super().get_prep_value(value)
2118        return self.to_python(value)
2119
2120    def get_db_prep_value(
2121        self, value: Any, connection: BaseDatabaseWrapper, prepared: bool = False
2122    ) -> Any:
2123        # Casts times into the format expected by the backend
2124        if not prepared:
2125            value = self.get_prep_value(value)
2126        return connection.ops.adapt_timefield_value(value)
2127
2128    def value_to_string(self, obj: Any) -> str:
2129        val = self.value_from_object(obj)
2130        return "" if val is None else val.isoformat()
2131
2132
2133class URLField(CharField):
2134    default_validators = [validators.URLValidator()]
2135    description = "URL"
2136
2137    def __init__(self, **kwargs: Any):
2138        kwargs.setdefault("max_length", 200)
2139        super().__init__(**kwargs)
2140
2141    def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
2142        name, path, args, kwargs = super().deconstruct()
2143        if kwargs.get("max_length") == 200:
2144            del kwargs["max_length"]
2145        return name, path, args, kwargs
2146
2147
2148class BinaryField(Field[bytes | memoryview]):
2149    description = "Raw binary data"
2150    empty_values = [None, b""]
2151
2152    def __init__(self, **kwargs: Any):
2153        super().__init__(**kwargs)
2154        if self.max_length is not None:
2155            self.validators.append(validators.MaxLengthValidator(self.max_length))
2156
2157    def preflight(self, **kwargs: Any) -> list[PreflightResult]:
2158        return [*super().preflight(**kwargs), *self._check_str_default_value()]
2159
2160    def _check_str_default_value(self) -> list[PreflightResult]:
2161        if self.has_default() and isinstance(self.default, str):
2162            return [
2163                PreflightResult(
2164                    fix="BinaryField's default cannot be a string. Use bytes "
2165                    "content instead.",
2166                    obj=self,
2167                    id="fields.filefield_upload_to_not_callable",
2168                )
2169            ]
2170        return []
2171
2172    def get_internal_type(self) -> str:
2173        return "BinaryField"
2174
2175    def get_placeholder(
2176        self, value: Any, compiler: SQLCompiler, connection: BaseDatabaseWrapper
2177    ) -> Any:
2178        return connection.ops.binary_placeholder_sql(value)
2179
2180    def get_default(self) -> Any:
2181        if self.has_default() and not callable(self.default):
2182            return self.default
2183        default = super().get_default()
2184        if default == "":
2185            return b""
2186        return default
2187
2188    def get_db_prep_value(
2189        self, value: Any, connection: BaseDatabaseWrapper, prepared: bool = False
2190    ) -> Any:
2191        value = super().get_db_prep_value(value, connection, prepared)
2192        if value is not None:
2193            return connection.Database.Binary(value)
2194        return value
2195
2196    def value_to_string(self, obj: Any) -> str:
2197        """Binary data is serialized as base64"""
2198        return b64encode(self.value_from_object(obj)).decode("ascii")
2199
2200    def to_python(self, value: Any) -> Any:
2201        # If it's a string, it should be base64-encoded data
2202        if isinstance(value, str):
2203            return memoryview(b64decode(value.encode("ascii")))
2204        return value
2205
2206
2207class UUIDField(Field[uuid.UUID]):
2208    default_error_messages = {
2209        "invalid": '"%(value)s" is not a valid UUID.',
2210    }
2211    description = "Universally unique identifier"
2212    empty_strings_allowed = False
2213
2214    def __init__(self, **kwargs: Any):
2215        kwargs["max_length"] = 32
2216        super().__init__(**kwargs)
2217
2218    def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
2219        name, path, args, kwargs = super().deconstruct()
2220        del kwargs["max_length"]
2221        return name, path, args, kwargs
2222
2223    def get_internal_type(self) -> str:
2224        return "UUIDField"
2225
2226    def get_prep_value(self, value: Any) -> Any:
2227        value = super().get_prep_value(value)
2228        return self.to_python(value)
2229
2230    def get_db_prep_value(
2231        self, value: Any, connection: BaseDatabaseWrapper, prepared: bool = False
2232    ) -> Any:
2233        if value is None:
2234            return None
2235        if not isinstance(value, uuid.UUID):
2236            value = self.to_python(value)
2237
2238        if connection.features.has_native_uuid_field:
2239            return value
2240        return value.hex
2241
2242    def to_python(self, value: Any) -> Any:
2243        if value is not None and not isinstance(value, uuid.UUID):
2244            input_form = "int" if isinstance(value, int) else "hex"
2245            try:
2246                return uuid.UUID(**{input_form: value})
2247            except (AttributeError, ValueError):
2248                raise exceptions.ValidationError(
2249                    self.error_messages["invalid"],
2250                    code="invalid",
2251                    params={"value": value},
2252                )
2253        return value
2254
2255
2256class PrimaryKeyField(BigIntegerField):
2257    db_returning = True
2258
2259    def __init__(self):
2260        super().__init__(required=False)
2261        self.primary_key = True
2262        self.auto_created = True
2263        # Adjust creation counter for auto-created fields
2264        # We need to undo the counter increment from Field.__init__ and use the auto counter
2265        Field.creation_counter -= 1  # Undo the increment
2266        self.creation_counter = Field.auto_creation_counter
2267        Field.auto_creation_counter -= 1
2268
2269    def preflight(self, **kwargs: Any) -> list[PreflightResult]:
2270        errors = super().preflight(**kwargs)
2271        # Remove the reserved_field_name_id error for 'id' field name since PrimaryKeyField is allowed to use it
2272        errors = [e for e in errors if e.id != "fields.reserved_field_name_id"]
2273        return errors
2274
2275    def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
2276        # PrimaryKeyField takes no parameters, so we return an empty kwargs dict
2277        return (
2278            self.name,
2279            "plain.models.PrimaryKeyField",
2280            cast(list[Any], []),
2281            cast(dict[str, Any], {}),
2282        )
2283
2284    def validate(self, value: Any, model_instance: Any) -> None:
2285        pass
2286
2287    def get_db_prep_value(
2288        self, value: Any, connection: BaseDatabaseWrapper, prepared: bool = False
2289    ) -> Any:
2290        if not prepared:
2291            value = self.get_prep_value(value)
2292            value = connection.ops.validate_autopk_value(value)
2293        return value
2294
2295    def get_internal_type(self) -> str:
2296        return "PrimaryKeyField"
2297
2298    def rel_db_type(self, connection: BaseDatabaseWrapper) -> str | None:
2299        return BigIntegerField().db_type(connection=connection)