Coverage for /Users/davegaeddert/Development/dropseed/plain/plain-models/plain/models/fields/__init__.py: 55%

1 import collections.abc

2 import copy

3 import datetime

4 import decimal

5 import operator

6 import uuid

7 import warnings

8 from base64 import b64decode, b64encode

9 from functools import partialmethod, total_ordering

10

11 from plain import exceptions, preflight, validators

12 from plain.models.constants import LOOKUP_SEP

13 from plain.models.db import connection, connections, router

14 from plain.models.enums import ChoicesMeta

15 from plain.models.query_utils import DeferredAttribute, RegisterLookupMixin

16 from plain.packages import packages

17 from plain.runtime import settings

18 from plain.utils import timezone

19 from plain.utils.datastructures import DictWrapper

20 from plain.utils.dateparse import (

21 parse_date,

22 parse_datetime,

23 parse_duration,

24 parse_time,

25)

26 from plain.utils.duration import duration_microseconds, duration_string

27 from plain.utils.functional import Promise, cached_property

28 from plain.utils.ipv6 import clean_ipv6_address

29 from 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

66 class Empty:

67 pass

68

69

70 class 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.

76 BLANK_CHOICE_DASH = [("", "---------")]

77

78

79 def _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

99 def _empty(of_cls):

100 new = Empty()

101 new.__class__ = of_cls

102 return new

103

104

105 def return_None():

106 return None

107

108

109@total_ordering

110 class 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

1004 class 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

1038 class 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

1141 class 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

1157 def _to_naive(value):

1158 if timezone.is_aware(value):

1159 value = timezone.make_naive(value, datetime.timezone.utc)

1160 return value

1161

1162

1163 def _get_naive_now():

1164 return _to_naive(timezone.now())

1165

1166

1167 class 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

1240 class 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 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

1359 class 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

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

1409 return value

1410

1411 try:

1412 parsed = parse_datetime(value)

1413 if parsed is not None:

1414 return parsed

1415 except ValueError:

1416 raise exceptions.ValidationError(

1417 self.error_messages["invalid_datetime"],

1418 code="invalid_datetime",

1419 params={"value": value},

1420 )

1421

1422 try:

1423 parsed = parse_date(value)

1424 if parsed is not None:

1425 return datetime.datetime(parsed.year, parsed.month, parsed.day)

1426 except ValueError:

1427 raise exceptions.ValidationError(

1428 self.error_messages["invalid_date"],

1429 code="invalid_date",

1430 params={"value": value},

1431 )

1432

1433 raise exceptions.ValidationError(

1434 self.error_messages["invalid"],

1435 code="invalid",

1436 params={"value": value},

1437 )

1438

1439 def pre_save(self, model_instance, add):

1440 if self.auto_now or (self.auto_now_add and add):

1441 value = timezone.now()

1442 setattr(model_instance, self.attname, value)

1443 return value

1444 else:

1445 return super().pre_save(model_instance, add)

1446

1447 # contribute_to_class is inherited from DateField, it registers

1448 # get_next_by_FOO and get_prev_by_FOO

1449

1450 def get_prep_value(self, value):

1451 value = super().get_prep_value(value)

1452 value = self.to_python(value)

1453 if value is not None and timezone.is_naive(value):

1454 # For backwards compatibility, interpret naive datetimes in local

1455 # time. This won't work during DST change, but we can't do much

1456 # about it, so we let the exceptions percolate up the call stack.

1457 try:

1458 name = f"{self.model.__name__}.{self.name}"

1459 except AttributeError:

1460 name = "(unbound)"

1461 warnings.warn(

1462 f"DateTimeField {name} received a naive datetime ({value})"

1463 " while time zone support is active.",

1464 RuntimeWarning,

1465 )

1466 default_timezone = timezone.get_default_timezone()

1467 value = timezone.make_aware(value, default_timezone)

1468 return value

1469

1470 def get_db_prep_value(self, value, connection, prepared=False):

1471 # Casts datetimes into the format expected by the backend

1472 if not prepared:

1473 value = self.get_prep_value(value)

1474 return connection.ops.adapt_datetimefield_value(value)

1475

1476 def value_to_string(self, obj):

1477 val = self.value_from_object(obj)

1478 return "" if val is None else val.isoformat()

1479

1480

1481 class DecimalField(Field):

1482 empty_strings_allowed = False

1483 default_error_messages = {

1484 "invalid": "“%(value)s” value must be a decimal number.",

1485 }

1486 description = "Decimal number"

1487

1488 def __init__(

1489 self,

1490 name=None,

1491 max_digits=None,

1492 decimal_places=None,

1493 **kwargs,

1494 ):

1495 self.max_digits, self.decimal_places = max_digits, decimal_places

1496 super().__init__(name, **kwargs)

1497

1498 def check(self, **kwargs):

1499 errors = super().check(**kwargs)

1500

1501 digits_errors = [

1502 *self._check_decimal_places(),

1503 *self._check_max_digits(),

1504 ]

1505 if not digits_errors:

1506 errors.extend(self._check_decimal_places_and_max_digits(**kwargs))

1507 else:

1508 errors.extend(digits_errors)

1509 return errors

1510

1511 def _check_decimal_places(self):

1512 try:

1513 decimal_places = int(self.decimal_places)

1514 if decimal_places < 0:

1515 raise ValueError()

1516 except TypeError:

1517 return [

1518 preflight.Error(

1519 "DecimalFields must define a 'decimal_places' attribute.",

1520 obj=self,

1521 id="fields.E130",

1522 )

1523 ]

1524 except ValueError:

1525 return [

1526 preflight.Error(

1527 "'decimal_places' must be a non-negative integer.",

1528 obj=self,

1529 id="fields.E131",

1530 )

1531 ]

1532 else:

1533 return []

1534

1535 def _check_max_digits(self):

1536 try:

1537 max_digits = int(self.max_digits)

1538 if max_digits <= 0:

1539 raise ValueError()

1540 except TypeError:

1541 return [

1542 preflight.Error(

1543 "DecimalFields must define a 'max_digits' attribute.",

1544 obj=self,

1545 id="fields.E132",

1546 )

1547 ]

1548 except ValueError:

1549 return [

1550 preflight.Error(

1551 "'max_digits' must be a positive integer.",

1552 obj=self,

1553 id="fields.E133",

1554 )

1555 ]

1556 else:

1557 return []

1558

1559 def _check_decimal_places_and_max_digits(self, **kwargs):

1560 if int(self.decimal_places) > int(self.max_digits):

1561 return [

1562 preflight.Error(

1563 "'max_digits' must be greater or equal to 'decimal_places'.",

1564 obj=self,

1565 id="fields.E134",

1566 )

1567 ]

1568 return []

1569

1570 @cached_property

1571 def validators(self):

1572 return super().validators + [

1573 validators.DecimalValidator(self.max_digits, self.decimal_places)

1574 ]

1575

1576 @cached_property

1577 def context(self):

1578 return decimal.Context(prec=self.max_digits)

1579

1580 def deconstruct(self):

1581 name, path, args, kwargs = super().deconstruct()

1582 if self.max_digits is not None:

1583 kwargs["max_digits"] = self.max_digits

1584 if self.decimal_places is not None:

1585 kwargs["decimal_places"] = self.decimal_places

1586 return name, path, args, kwargs

1587

1588 def get_internal_type(self):

1589 return "DecimalField"

1590

1591 def to_python(self, value):

1592 if value is None:

1593 return value

1594 try:

1595 if isinstance(value, float):

1596 decimal_value = self.context.create_decimal_from_float(value)

1597 else:

1598 decimal_value = decimal.Decimal(value)

1599 except (decimal.InvalidOperation, TypeError, ValueError):

1600 raise exceptions.ValidationError(

1601 self.error_messages["invalid"],

1602 code="invalid",

1603 params={"value": value},

1604 )

1605 if not decimal_value.is_finite():

1606 raise exceptions.ValidationError(

1607 self.error_messages["invalid"],

1608 code="invalid",

1609 params={"value": value},

1610 )

1611 return decimal_value

1612

1613 def get_db_prep_value(self, value, connection, prepared=False):

1614 if not prepared:

1615 value = self.get_prep_value(value)

1616 if hasattr(value, "as_sql"):

1617 return value

1618 return connection.ops.adapt_decimalfield_value(

1619 value, self.max_digits, self.decimal_places

1620 )

1621

1622 def get_prep_value(self, value):

1623 value = super().get_prep_value(value)

1624 return self.to_python(value)

1625

1626

1627 class DurationField(Field):

1628 """

1629 Store timedelta objects.

1630

1631 Use interval on PostgreSQL, INTERVAL DAY TO SECOND on Oracle, and bigint

1632 of microseconds on other databases.

1633 """

1634

1635 empty_strings_allowed = False

1636 default_error_messages = {

1637 "invalid": "“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[.uuuuuu] format.",

1638 }

1639 description = "Duration"

1640

1641 def get_internal_type(self):

1642 return "DurationField"

1643

1644 def to_python(self, value):

1645 if value is None:

1646 return value

1647 if isinstance(value, datetime.timedelta):

1648 return value

1649 try:

1650 parsed = parse_duration(value)

1651 except ValueError:

1652 pass

1653 else:

1654 if parsed is not None:

1655 return parsed

1656

1657 raise exceptions.ValidationError(

1658 self.error_messages["invalid"],

1659 code="invalid",

1660 params={"value": value},

1661 )

1662

1663 def get_db_prep_value(self, value, connection, prepared=False):

1664 if connection.features.has_native_duration_field:

1665 return value

1666 if value is None:

1667 return None

1668 return duration_microseconds(value)

1669

1670 def get_db_converters(self, connection):

1671 converters = []

1672 if not connection.features.has_native_duration_field:

1673 converters.append(connection.ops.convert_durationfield_value)

1674 return converters + super().get_db_converters(connection)

1675

1676 def value_to_string(self, obj):

1677 val = self.value_from_object(obj)

1678 return "" if val is None else duration_string(val)

1679

1680

1681 class EmailField(CharField):

1682 default_validators = [validators.validate_email]

1683 description = "Email address"

1684

1685 def __init__(self, *args, **kwargs):

1686 # max_length=254 to be compliant with RFCs 3696 and 5321

1687 kwargs.setdefault("max_length", 254)

1688 super().__init__(*args, **kwargs)

1689

1690 def deconstruct(self):

1691 name, path, args, kwargs = super().deconstruct()

1692 # We do not exclude max_length if it matches default as we want to change

1693 # the default in future.

1694 return name, path, args, kwargs

1695

1696

1697 class FloatField(Field):

1698 empty_strings_allowed = False

1699 default_error_messages = {

1700 "invalid": "“%(value)s” value must be a float.",

1701 }

1702 description = "Floating point number"

1703

1704 def get_prep_value(self, value):

1705 value = super().get_prep_value(value)

1706 if value is None:

1707 return None

1708 try:

1709 return float(value)

1710 except (TypeError, ValueError) as e:

1711 raise e.__class__(

1712 f"Field '{self.name}' expected a number but got {value! r}.",

1713 ) from e

1714

1715 def get_internal_type(self):

1716 return "FloatField"

1717

1718 def to_python(self, value):

1719 if value is None:

1720 return value

1721 try:

1722 return float(value)

1723 except (TypeError, ValueError):

1724 raise exceptions.ValidationError(

1725 self.error_messages["invalid"],

1726 code="invalid",

1727 params={"value": value},

1728 )

1729

1730

1731 class IntegerField(Field):

1732 empty_strings_allowed = False

1733 default_error_messages = {

1734 "invalid": "“%(value)s” value must be an integer.",

1735 }

1736 description = "Integer"

1737

1738 def check(self, **kwargs):

1739 return [

1740 *super().check(**kwargs),

1741 *self._check_max_length_warning(),

1742 ]

1743

1744 def _check_max_length_warning(self):

1745 if self.max_length is not None:

1746 return [

1747 preflight.Warning(

1748 "'max_length' is ignored when used with %s."

1749 % self.__class__.__name__,

1750 hint="Remove 'max_length' from field",

1751 obj=self,

1752 id="fields.W122",

1753 )

1754 ]

1755 return []

1756

1757 @cached_property

1758 def validators(self):

1759 # These validators can't be added at field initialization time since

1760 # they're based on values retrieved from `connection`.

1761 validators_ = super().validators

1762 internal_type = self.get_internal_type()

1763 min_value, max_value = connection.ops.integer_field_range(internal_type)

1764 if min_value is not None and not any(

1765 (

1766 isinstance(validator, validators.MinValueValidator)

1767 and (

1768 validator.limit_value()

1769 if callable(validator.limit_value)

1770 else validator.limit_value

1771 )

1772 >= min_value

1773 )

1774 for validator in validators_

1775 ):

1776 validators_.append(validators.MinValueValidator(min_value))

1777 if max_value is not None and not any(

1778 (

1779 isinstance(validator, validators.MaxValueValidator)

1780 and (

1781 validator.limit_value()

1782 if callable(validator.limit_value)

1783 else validator.limit_value

1784 )

1785 <= max_value

1786 )

1787 for validator in validators_

1788 ):

1789 validators_.append(validators.MaxValueValidator(max_value))

1790 return validators_

1791

1792 def get_prep_value(self, value):

1793 value = super().get_prep_value(value)

1794 if value is None:

1795 return None

1796 try:

1797 return int(value)

1798 except (TypeError, ValueError) as e:

1799 raise e.__class__(

1800 f"Field '{self.name}' expected a number but got {value! r}.",

1801 ) from e

1802

1803 def get_db_prep_value(self, value, connection, prepared=False):

1804 value = super().get_db_prep_value(value, connection, prepared)

1805 return connection.ops.adapt_integerfield_value(value, self.get_internal_type())

1806

1807 def get_internal_type(self):

1808 return "IntegerField"

1809

1810 def to_python(self, value):

1811 if value is None:

1812 return value

1813 try:

1814 return int(value)

1815 except (TypeError, ValueError):

1816 raise exceptions.ValidationError(

1817 self.error_messages["invalid"],

1818 code="invalid",

1819 params={"value": value},

1820 )

1821

1822

1823 class BigIntegerField(IntegerField):

1824 description = "Big (8 byte) integer"

1825 MAX_BIGINT = 9223372036854775807

1826

1827 def get_internal_type(self):

1828 return "BigIntegerField"

1829

1830

1831 class SmallIntegerField(IntegerField):

1832 description = "Small integer"

1833

1834 def get_internal_type(self):

1835 return "SmallIntegerField"

1836

1837

1838 class IPAddressField(Field):

1839 empty_strings_allowed = False

1840 description = "IPv4 address"

1841 system_check_removed_details = {

1842 "msg": (

1843 "IPAddressField has been removed except for support in "

1844 "historical migrations."

1845 ),

1846 "hint": "Use GenericIPAddressField instead.",

1847 "id": "fields.E900",

1848 }

1849

1850 def __init__(self, *args, **kwargs):

1851 kwargs["max_length"] = 15

1852 super().__init__(*args, **kwargs)

1853

1854 def deconstruct(self):

1855 name, path, args, kwargs = super().deconstruct()

1856 del kwargs["max_length"]

1857 return name, path, args, kwargs

1858

1859 def get_prep_value(self, value):

1860 value = super().get_prep_value(value)

1861 if value is None:

1862 return None

1863 return str(value)

1864

1865 def get_internal_type(self):

1866 return "IPAddressField"

1867

1868

1869 class GenericIPAddressField(Field):

1870 empty_strings_allowed = False

1871 description = "IP address"

1872 default_error_messages = {}

1873

1874 def __init__(

1875 self,

1876 name=None,

1877 protocol="both",

1878 unpack_ipv4=False,

1879 *args,

1880 **kwargs,

1881 ):

1882 self.unpack_ipv4 = unpack_ipv4

1883 self.protocol = protocol

1884 (

1885 self.default_validators,

1886 invalid_error_message,

1887 ) = validators.ip_address_validators(protocol, unpack_ipv4)

1888 self.default_error_messages["invalid"] = invalid_error_message

1889 kwargs["max_length"] = 39

1890 super().__init__(name, *args, **kwargs)

1891

1892 def check(self, **kwargs):

1893 return [

1894 *super().check(**kwargs),

1895 *self._check_blank_and_null_values(**kwargs),

1896 ]

1897

1898 def _check_blank_and_null_values(self, **kwargs):

1899 if not getattr(self, "null", False) and getattr(self, "blank", False):

1900 return [

1901 preflight.Error(

1902 "GenericIPAddressFields cannot have blank=True if null=False, "

1903 "as blank values are stored as nulls.",

1904 obj=self,

1905 id="fields.E150",

1906 )

1907 ]

1908 return []

1909

1910 def deconstruct(self):

1911 name, path, args, kwargs = super().deconstruct()

1912 if self.unpack_ipv4 is not False:

1913 kwargs["unpack_ipv4"] = self.unpack_ipv4

1914 if self.protocol != "both":

1915 kwargs["protocol"] = self.protocol

1916 if kwargs.get("max_length") == 39:

1917 del kwargs["max_length"]

1918 return name, path, args, kwargs

1919

1920 def get_internal_type(self):

1921 return "GenericIPAddressField"

1922

1923 def to_python(self, value):

1924 if value is None:

1925 return None

1926 if not isinstance(value, str):

1927 value = str(value)

1928 value = value.strip()

1929 if ":" in value:

1930 return clean_ipv6_address(

1931 value, self.unpack_ipv4, self.error_messages["invalid"]

1932 )

1933 return value

1934

1935 def get_db_prep_value(self, value, connection, prepared=False):

1936 if not prepared:

1937 value = self.get_prep_value(value)

1938 return connection.ops.adapt_ipaddressfield_value(value)

1939

1940 def get_prep_value(self, value):

1941 value = super().get_prep_value(value)

1942 if value is None:

1943 return None

1944 if value and ":" in value:

1945 try:

1946 return clean_ipv6_address(value, self.unpack_ipv4)

1947 except exceptions.ValidationError:

1948 pass

1949 return str(value)

1950

1951

1952 class NullBooleanField(BooleanField):

1953 default_error_messages = {

1954 "invalid": "“%(value)s” value must be either None, True or False.",

1955 "invalid_nullable": "“%(value)s” value must be either None, True or False.",

1956 }

1957 description = "Boolean (Either True, False or None)"

1958 system_check_removed_details = {

1959 "msg": (

1960 "NullBooleanField is removed except for support in historical "

1961 "migrations."

1962 ),

1963 "hint": "Use BooleanField(null=True) instead.",

1964 "id": "fields.E903",

1965 }

1966

1967 def __init__(self, *args, **kwargs):

1968 kwargs["null"] = True

1969 kwargs["blank"] = True

1970 super().__init__(*args, **kwargs)

1971

1972 def deconstruct(self):

1973 name, path, args, kwargs = super().deconstruct()

1974 del kwargs["null"]

1975 del kwargs["blank"]

1976 return name, path, args, kwargs

1977

1978

1979 class PositiveIntegerRelDbTypeMixin:

1980 def __init_subclass__(cls, **kwargs):

1981 super().__init_subclass__(**kwargs)

1982 if not hasattr(cls, "integer_field_class"):

1983 cls.integer_field_class = next(

1984 (

1985 parent

1986 for parent in cls.__mro__[1:]

1987 if issubclass(parent, IntegerField)

1988 ),

1989 None,

1990 )

1991

1992 def rel_db_type(self, connection):

1993 """

1994 Return the data type that a related field pointing to this field should

1995 use. In most cases, a foreign key pointing to a positive integer

1996 primary key will have an integer column data type but some databases

1997 (e.g. MySQL) have an unsigned integer type. In that case

1998 (related_fields_match_type=True), the primary key should return its

1999 db_type.

2000 """

2001 if connection.features.related_fields_match_type:

2002 return self.db_type(connection)

2003 else:

2004 return self.integer_field_class().db_type(connection=connection)

2005

2006

2007 class PositiveBigIntegerField(PositiveIntegerRelDbTypeMixin, BigIntegerField):

2008 description = "Positive big integer"

2009

2010 def get_internal_type(self):

2011 return "PositiveBigIntegerField"

2012

2013

2014 class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):

2015 description = "Positive integer"

2016

2017 def get_internal_type(self):

2018 return "PositiveIntegerField"

2019

2020

2021 class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, SmallIntegerField):

2022 description = "Positive small integer"

2023

2024 def get_internal_type(self):

2025 return "PositiveSmallIntegerField"

2026

2027

2028 class SlugField(CharField):

2029 default_validators = [validators.validate_slug]

2030 description = "Slug (up to %(max_length)s)"

2031

2032 def __init__(

2033 self, *args, max_length=50, db_index=True, allow_unicode=False, **kwargs

2034 ):

2035 self.allow_unicode = allow_unicode

2036 if self.allow_unicode:

2037 self.default_validators = [validators.validate_unicode_slug]

2038 super().__init__(*args, max_length=max_length, db_index=db_index, **kwargs)

2039

2040 def deconstruct(self):

2041 name, path, args, kwargs = super().deconstruct()

2042 if kwargs.get("max_length") == 50:

2043 del kwargs["max_length"]

2044 if self.db_index is False:

2045 kwargs["db_index"] = False

2046 else:

2047 del kwargs["db_index"]

2048 if self.allow_unicode is not False:

2049 kwargs["allow_unicode"] = self.allow_unicode

2050 return name, path, args, kwargs

2051

2052 def get_internal_type(self):

2053 return "SlugField"

2054

2055

2056 class TextField(Field):

2057 description = "Text"

2058

2059 def __init__(self, *args, db_collation=None, **kwargs):

2060 super().__init__(*args, **kwargs)

2061 self.db_collation = db_collation

2062

2063 def check(self, **kwargs):

2064 databases = kwargs.get("databases") or []

2065 return [

2066 *super().check(**kwargs),

2067 *self._check_db_collation(databases),

2068 ]

2069

2070 def _check_db_collation(self, databases):

2071 errors = []

2072 for db in databases:

2073 if not router.allow_migrate_model(db, self.model):

2074 continue

2075 connection = connections[db]

2076 if not (

2077 self.db_collation is None

2078 or "supports_collation_on_textfield"

2079 in self.model._meta.required_db_features

2080 or connection.features.supports_collation_on_textfield

2081 ):

2082 errors.append(

2083 preflight.Error(

2084 "%s does not support a database collation on "

2085 "TextFields." % connection.display_name,

2086 obj=self,

2087 id="fields.E190",

2088 ),

2089 )

2090 return errors

2091

2092 def db_parameters(self, connection):

2093 db_params = super().db_parameters(connection)

2094 db_params["collation"] = self.db_collation

2095 return db_params

2096

2097 def get_internal_type(self):

2098 return "TextField"

2099

2100 def to_python(self, value):

2101 if isinstance(value, str) or value is None:

2102 return value

2103 return str(value)

2104

2105 def get_prep_value(self, value):

2106 value = super().get_prep_value(value)

2107 return self.to_python(value)

2108

2109 def deconstruct(self):

2110 name, path, args, kwargs = super().deconstruct()

2111 if self.db_collation:

2112 kwargs["db_collation"] = self.db_collation

2113 return name, path, args, kwargs

2114

2115

2116 class TimeField(DateTimeCheckMixin, Field):

2117 empty_strings_allowed = False

2118 default_error_messages = {

2119 "invalid": "“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] format.",

2120 "invalid_time": "“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an invalid time.",

2121 }

2122 description = "Time"

2123

2124 def __init__(self, name=None, auto_now=False, auto_now_add=False, **kwargs):

2125 self.auto_now, self.auto_now_add = auto_now, auto_now_add

2126 if auto_now or auto_now_add:

2127 kwargs["editable"] = False

2128 kwargs["blank"] = True

2129 super().__init__(name, **kwargs)

2130

2131 def _check_fix_default_value(self):

2132 """

2133 Warn that using an actual date or datetime value is probably wrong;

2134 it's only evaluated on server startup.

2135 """

2136 if not self.has_default():

2137 return []

2138

2139 value = self.default

2140 if isinstance(value, datetime.datetime):

2141 now = None

2142 elif isinstance(value, datetime.time):

2143 now = _get_naive_now()

2144 # This will not use the right date in the race condition where now

2145 # is just before the date change and value is just past 0:00.

2146 value = datetime.datetime.combine(now.date(), value)

2147 else:

2148 # No explicit time / datetime value -- no checks necessary

2149 return []

2150 # At this point, value is a datetime object.

2151 return self._check_if_value_fixed(value, now=now)

2152

2153 def deconstruct(self):

2154 name, path, args, kwargs = super().deconstruct()

2155 if self.auto_now is not False:

2156 kwargs["auto_now"] = self.auto_now

2157 if self.auto_now_add is not False:

2158 kwargs["auto_now_add"] = self.auto_now_add

2159 if self.auto_now or self.auto_now_add:

2160 del kwargs["blank"]

2161 del kwargs["editable"]

2162 return name, path, args, kwargs

2163

2164 def get_internal_type(self):

2165 return "TimeField"

2166

2167 def to_python(self, value):

2168 if value is None:

2169 return None

2170 if isinstance(value, datetime.time):

2171 return value

2172 if isinstance(value, datetime.datetime):

2173 # Not usually a good idea to pass in a datetime here (it loses

2174 # information), but this can be a side-effect of interacting with a

2175 # database backend (e.g. Oracle), so we'll be accommodating.

2176 return value.time()

2177

2178 try:

2179 parsed = parse_time(value)

2180 if parsed is not None:

2181 return parsed

2182 except ValueError:

2183 raise exceptions.ValidationError(

2184 self.error_messages["invalid_time"],

2185 code="invalid_time",

2186 params={"value": value},

2187 )

2188

2189 raise exceptions.ValidationError(

2190 self.error_messages["invalid"],

2191 code="invalid",

2192 params={"value": value},

2193 )

2194

2195 def pre_save(self, model_instance, add):

2196 if self.auto_now or (self.auto_now_add and add):

2197 value = datetime.datetime.now().time()

2198 setattr(model_instance, self.attname, value)

2199 return value

2200 else:

2201 return super().pre_save(model_instance, add)

2202

2203 def get_prep_value(self, value):

2204 value = super().get_prep_value(value)

2205 return self.to_python(value)

2206

2207 def get_db_prep_value(self, value, connection, prepared=False):

2208 # Casts times into the format expected by the backend

2209 if not prepared:

2210 value = self.get_prep_value(value)

2211 return connection.ops.adapt_timefield_value(value)

2212

2213 def value_to_string(self, obj):

2214 val = self.value_from_object(obj)

2215 return "" if val is None else val.isoformat()

2216

2217

2218 class URLField(CharField):

2219 default_validators = [validators.URLValidator()]

2220 description = "URL"

2221

2222 def __init__(self, name=None, **kwargs):

2223 kwargs.setdefault("max_length", 200)

2224 super().__init__(name, **kwargs)

2225

2226 def deconstruct(self):

2227 name, path, args, kwargs = super().deconstruct()

2228 if kwargs.get("max_length") == 200:

2229 del kwargs["max_length"]

2230 return name, path, args, kwargs

2231

2232

2233 class BinaryField(Field):

2234 description = "Raw binary data"

2235 empty_values = [None, b""]

2236

2237 def __init__(self, *args, **kwargs):

2238 kwargs.setdefault("editable", False)

2239 super().__init__(*args, **kwargs)

2240 if self.max_length is not None:

2241 self.validators.append(validators.MaxLengthValidator(self.max_length))

2242

2243 def check(self, **kwargs):

2244 return [*super().check(**kwargs), *self._check_str_default_value()]

2245

2246 def _check_str_default_value(self):

2247 if self.has_default() and isinstance(self.default, str):

2248 return [

2249 preflight.Error(

2250 "BinaryField's default cannot be a string. Use bytes "

2251 "content instead.",

2252 obj=self,

2253 id="fields.E170",

2254 )

2255 ]

2256 return []

2257

2258 def deconstruct(self):

2259 name, path, args, kwargs = super().deconstruct()

2260 if self.editable:

2261 kwargs["editable"] = True

2262 else:

2263 del kwargs["editable"]

2264 return name, path, args, kwargs

2265

2266 def get_internal_type(self):

2267 return "BinaryField"

2268

2269 def get_placeholder(self, value, compiler, connection):

2270 return connection.ops.binary_placeholder_sql(value)

2271

2272 def get_default(self):

2273 if self.has_default() and not callable(self.default):

2274 return self.default

2275 default = super().get_default()

2276 if default == "":

2277 return b""

2278 return default

2279

2280 def get_db_prep_value(self, value, connection, prepared=False):

2281 value = super().get_db_prep_value(value, connection, prepared)

2282 if value is not None:

2283 return connection.Database.Binary(value)

2284 return value

2285

2286 def value_to_string(self, obj):

2287 """Binary data is serialized as base64"""

2288 return b64encode(self.value_from_object(obj)).decode("ascii")

2289

2290 def to_python(self, value):

2291 # If it's a string, it should be base64-encoded data

2292 if isinstance(value, str):

2293 return memoryview(b64decode(value.encode("ascii")))

2294 return value

2295

2296

2297 class UUIDField(Field):

2298 default_error_messages = {

2299 "invalid": "“%(value)s” is not a valid UUID.",

2300 }

2301 description = "Universally unique identifier"

2302 empty_strings_allowed = False

2303

2304 def __init__(self, **kwargs):

2305 kwargs["max_length"] = 32

2306 super().__init__(**kwargs)

2307

2308 def deconstruct(self):

2309 name, path, args, kwargs = super().deconstruct()

2310 del kwargs["max_length"]

2311 return name, path, args, kwargs

2312

2313 def get_internal_type(self):

2314 return "UUIDField"

2315

2316 def get_prep_value(self, value):

2317 value = super().get_prep_value(value)

2318 return self.to_python(value)

2319

2320 def get_db_prep_value(self, value, connection, prepared=False):

2321 if value is None:

2322 return None

2323 if not isinstance(value, uuid.UUID):

2324 value = self.to_python(value)

2325

2326 if connection.features.has_native_uuid_field:

2327 return value

2328 return value.hex

2329

2330 def to_python(self, value):

2331 if value is not None and not isinstance(value, uuid.UUID):

2332 input_form = "int" if isinstance(value, int) else "hex"

2333 try:

2334 return uuid.UUID(**{input_form: value})

2335 except (AttributeError, ValueError):

2336 raise exceptions.ValidationError(

2337 self.error_messages["invalid"],

2338 code="invalid",

2339 params={"value": value},

2340 )

2341 return value

2342

2343

2344 class AutoFieldMixin:

2345 db_returning = True

2346

2347 def __init__(self, *args, **kwargs):

2348 kwargs["blank"] = True

2349 super().__init__(*args, **kwargs)

2350

2351 def check(self, **kwargs):

2352 return [

2353 *super().check(**kwargs),

2354 *self._check_primary_key(),

2355 ]

2356

2357 def _check_primary_key(self):

2358 if not self.primary_key:

2359 return [

2360 preflight.Error(

2361 "AutoFields must set primary_key=True.",

2362 obj=self,

2363 id="fields.E100",

2364 ),

2365 ]

2366 else:

2367 return []

2368

2369 def deconstruct(self):

2370 name, path, args, kwargs = super().deconstruct()

2371 del kwargs["blank"]

2372 kwargs["primary_key"] = True

2373 return name, path, args, kwargs

2374

2375 def validate(self, value, model_instance):

2376 pass

2377

2378 def get_db_prep_value(self, value, connection, prepared=False):

2379 if not prepared:

2380 value = self.get_prep_value(value)

2381 value = connection.ops.validate_autopk_value(value)

2382 return value

2383

2384 def contribute_to_class(self, cls, name, **kwargs):

2385 if cls._meta.auto_field:

2386 raise ValueError(

2387 "Model %s can't have more than one auto-generated field."

2388 % cls._meta.label

2389 )

2390 super().contribute_to_class(cls, name, **kwargs)

2391 cls._meta.auto_field = self

2392

2393

2394 class AutoFieldMeta(type):

2395 """

2396 Metaclass to maintain backward inheritance compatibility for AutoField.

2397

2398 It is intended that AutoFieldMixin become public API when it is possible to

2399 create a non-integer automatically-generated field using column defaults

2400 stored in the database.

2401

2402 In many areas Plain also relies on using isinstance() to check for an

2403 automatically-generated field as a subclass of AutoField. A new flag needs

2404 to be implemented on Field to be used instead.

2405

2406 When these issues have been addressed, this metaclass could be used to

2407 deprecate inheritance from AutoField and use of isinstance() with AutoField

2408 for detecting automatically-generated fields.

2409 """

2410

2411 @property

2412 def _subclasses(self):

2413 return (BigAutoField, SmallAutoField)

2414

2415 def __instancecheck__(self, instance):

2416 return isinstance(instance, self._subclasses) or super().__instancecheck__(

2417 instance

2418 )

2419

2420 def __subclasscheck__(self, subclass):

2421 return issubclass(subclass, self._subclasses) or super().__subclasscheck__(

2422 subclass

2423 )

2424

2425

2426 class AutoField(AutoFieldMixin, IntegerField, metaclass=AutoFieldMeta):

2427 def get_internal_type(self):

2428 return "AutoField"

2429

2430 def rel_db_type(self, connection):

2431 return IntegerField().db_type(connection=connection)

2432

2433

2434 class BigAutoField(AutoFieldMixin, BigIntegerField):

2435 def get_internal_type(self):

2436 return "BigAutoField"

2437

2438 def rel_db_type(self, connection):

2439 return BigIntegerField().db_type(connection=connection)

2440

2441

2442 class SmallAutoField(AutoFieldMixin, SmallIntegerField):

2443 def get_internal_type(self):

2444 return "SmallAutoField"

2445

2446 def rel_db_type(self, connection):

2447 return SmallIntegerField().db_type(connection=connection)