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