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