plain.models 0.45.0__py3-none-any.whl → 0.46.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain/models/CHANGELOG.md +13 -0
- plain/models/backends/base/validation.py +1 -1
- plain/models/backends/mysql/base.py +0 -1
- plain/models/backends/mysql/validation.py +16 -16
- plain/models/backends/postgresql/base.py +0 -1
- plain/models/backends/postgresql/operations.py +1 -2
- plain/models/backends/sqlite3/base.py +0 -1
- plain/models/base.py +206 -234
- plain/models/config.py +1 -9
- plain/models/fields/__init__.py +117 -227
- plain/models/fields/json.py +22 -22
- plain/models/fields/mixins.py +11 -10
- plain/models/fields/related.py +131 -119
- plain/models/migrations/state.py +1 -1
- plain/models/preflight.py +105 -98
- {plain_models-0.45.0.dist-info → plain_models-0.46.0.dist-info}/METADATA +1 -1
- {plain_models-0.45.0.dist-info → plain_models-0.46.0.dist-info}/RECORD +20 -20
- {plain_models-0.45.0.dist-info → plain_models-0.46.0.dist-info}/WHEEL +0 -0
- {plain_models-0.45.0.dist-info → plain_models-0.46.0.dist-info}/entry_points.txt +0 -0
- {plain_models-0.45.0.dist-info → plain_models-0.46.0.dist-info}/licenses/LICENSE +0 -0
plain/models/fields/__init__.py
CHANGED
@@ -9,11 +9,12 @@ import warnings
|
|
9
9
|
from base64 import b64decode, b64encode
|
10
10
|
from functools import cached_property, partialmethod, total_ordering
|
11
11
|
|
12
|
-
from plain import exceptions,
|
12
|
+
from plain import exceptions, validators
|
13
13
|
from plain.models.constants import LOOKUP_SEP
|
14
14
|
from plain.models.db import db_connection
|
15
15
|
from plain.models.enums import ChoicesMeta
|
16
16
|
from plain.models.query_utils import DeferredAttribute, RegisterLookupMixin
|
17
|
+
from plain.preflight import PreflightResult
|
17
18
|
from plain.utils import timezone
|
18
19
|
from plain.utils.datastructures import DictWrapper
|
19
20
|
from plain.utils.dateparse import (
|
@@ -36,7 +37,6 @@ __all__ = [
|
|
36
37
|
"BinaryField",
|
37
38
|
"BooleanField",
|
38
39
|
"CharField",
|
39
|
-
"CommaSeparatedIntegerField",
|
40
40
|
"DateField",
|
41
41
|
"DateTimeField",
|
42
42
|
"DecimalField",
|
@@ -46,10 +46,8 @@ __all__ = [
|
|
46
46
|
"Field",
|
47
47
|
"FloatField",
|
48
48
|
"GenericIPAddressField",
|
49
|
-
"IPAddressField",
|
50
49
|
"IntegerField",
|
51
50
|
"NOT_PROVIDED",
|
52
|
-
"NullBooleanField",
|
53
51
|
"PositiveBigIntegerField",
|
54
52
|
"PositiveIntegerField",
|
55
53
|
"PositiveSmallIntegerField",
|
@@ -125,8 +123,6 @@ class Field(RegisterLookupMixin):
|
|
125
123
|
"required": "This field is be required.",
|
126
124
|
"unique": "A %(model_name)s with this %(field_label)s already exists.",
|
127
125
|
}
|
128
|
-
system_check_deprecated_details = None
|
129
|
-
system_check_removed_details = None
|
130
126
|
|
131
127
|
# Attributes that don't affect a column definition.
|
132
128
|
# These attributes are ignored when altering the field.
|
@@ -218,15 +214,14 @@ class Field(RegisterLookupMixin):
|
|
218
214
|
return f"<{path}: {name}>"
|
219
215
|
return f"<{path}>"
|
220
216
|
|
221
|
-
def
|
217
|
+
def preflight(self, **kwargs):
|
222
218
|
return [
|
223
219
|
*self._check_field_name(),
|
224
220
|
*self._check_choices(),
|
225
|
-
*self._check_db_comment(
|
221
|
+
*self._check_db_comment(),
|
226
222
|
*self._check_null_allowed_for_primary_keys(),
|
227
|
-
*self._check_backend_specific_checks(
|
223
|
+
*self._check_backend_specific_checks(),
|
228
224
|
*self._check_validators(),
|
229
|
-
*self._check_deprecation_details(),
|
230
225
|
]
|
231
226
|
|
232
227
|
def _check_field_name(self):
|
@@ -236,26 +231,26 @@ class Field(RegisterLookupMixin):
|
|
236
231
|
"""
|
237
232
|
if self.name.endswith("_"):
|
238
233
|
return [
|
239
|
-
|
240
|
-
"Field names must not end with an underscore.",
|
234
|
+
PreflightResult(
|
235
|
+
fix="Field names must not end with an underscore.",
|
241
236
|
obj=self,
|
242
|
-
id="fields.
|
237
|
+
id="fields.name_ends_with_underscore",
|
243
238
|
)
|
244
239
|
]
|
245
240
|
elif LOOKUP_SEP in self.name:
|
246
241
|
return [
|
247
|
-
|
248
|
-
f'Field names must not contain "{LOOKUP_SEP}".',
|
242
|
+
PreflightResult(
|
243
|
+
fix=f'Field names must not contain "{LOOKUP_SEP}".',
|
249
244
|
obj=self,
|
250
|
-
id="fields.
|
245
|
+
id="fields.name_contains_lookup_separator",
|
251
246
|
)
|
252
247
|
]
|
253
248
|
elif self.name == "id":
|
254
249
|
return [
|
255
|
-
|
256
|
-
"'id' is a reserved word that cannot be used as a field name.",
|
250
|
+
PreflightResult(
|
251
|
+
fix="'id' is a reserved word that cannot be used as a field name.",
|
257
252
|
obj=self,
|
258
|
-
id="fields.
|
253
|
+
id="fields.reserved_field_name_id",
|
259
254
|
)
|
260
255
|
]
|
261
256
|
else:
|
@@ -271,10 +266,10 @@ class Field(RegisterLookupMixin):
|
|
271
266
|
|
272
267
|
if not is_iterable(self.choices) or isinstance(self.choices, str):
|
273
268
|
return [
|
274
|
-
|
275
|
-
"'choices' must be an iterable (e.g., a list or tuple).",
|
269
|
+
PreflightResult(
|
270
|
+
fix="'choices' must be an iterable (e.g., a list or tuple).",
|
276
271
|
obj=self,
|
277
|
-
id="fields.
|
272
|
+
id="fields.choices_not_iterable",
|
278
273
|
)
|
279
274
|
]
|
280
275
|
|
@@ -319,26 +314,26 @@ class Field(RegisterLookupMixin):
|
|
319
314
|
else:
|
320
315
|
if self.max_length is not None and choice_max_length > self.max_length:
|
321
316
|
return [
|
322
|
-
|
323
|
-
"'max_length' is too small to fit the longest value " # noqa: UP031
|
317
|
+
PreflightResult(
|
318
|
+
fix="'max_length' is too small to fit the longest value " # noqa: UP031
|
324
319
|
"in 'choices' (%d characters)." % choice_max_length,
|
325
320
|
obj=self,
|
326
|
-
id="fields.
|
321
|
+
id="fields.max_length_too_small_for_choices",
|
327
322
|
),
|
328
323
|
]
|
329
324
|
return []
|
330
325
|
|
331
326
|
return [
|
332
|
-
|
333
|
-
"'choices' must be an iterable containing "
|
327
|
+
PreflightResult(
|
328
|
+
fix="'choices' must be an iterable containing "
|
334
329
|
"(actual value, human readable name) tuples.",
|
335
330
|
obj=self,
|
336
|
-
id="fields.
|
331
|
+
id="fields.choices_invalid_format",
|
337
332
|
)
|
338
333
|
]
|
339
334
|
|
340
|
-
def _check_db_comment(self
|
341
|
-
if not self.db_comment
|
335
|
+
def _check_db_comment(self):
|
336
|
+
if not self.db_comment:
|
342
337
|
return []
|
343
338
|
errors = []
|
344
339
|
if not (
|
@@ -346,11 +341,12 @@ class Field(RegisterLookupMixin):
|
|
346
341
|
or "supports_comments" in self.model._meta.required_db_features
|
347
342
|
):
|
348
343
|
errors.append(
|
349
|
-
|
350
|
-
f"{db_connection.display_name} does not support comments on "
|
344
|
+
PreflightResult(
|
345
|
+
fix=f"{db_connection.display_name} does not support comments on "
|
351
346
|
f"columns (db_comment).",
|
352
347
|
obj=self,
|
353
|
-
id="fields.
|
348
|
+
id="fields.db_comment_unsupported",
|
349
|
+
warning=True,
|
354
350
|
)
|
355
351
|
)
|
356
352
|
return errors
|
@@ -361,24 +357,20 @@ class Field(RegisterLookupMixin):
|
|
361
357
|
# consider NULL and '' to be equal (and thus set up
|
362
358
|
# character-based fields a little differently).
|
363
359
|
return [
|
364
|
-
|
365
|
-
"Primary keys must not have allow_null=True."
|
366
|
-
|
367
|
-
|
368
|
-
"remove primary_key=True argument."
|
369
|
-
),
|
360
|
+
PreflightResult(
|
361
|
+
fix="Primary keys must not have allow_null=True. "
|
362
|
+
"Set allow_null=False on the field, or "
|
363
|
+
"remove primary_key=True argument.",
|
370
364
|
obj=self,
|
371
|
-
id="fields.
|
365
|
+
id="fields.primary_key_allows_null",
|
372
366
|
)
|
373
367
|
]
|
374
368
|
else:
|
375
369
|
return []
|
376
370
|
|
377
|
-
def _check_backend_specific_checks(self
|
378
|
-
if not database:
|
379
|
-
return []
|
371
|
+
def _check_backend_specific_checks(self):
|
380
372
|
errors = []
|
381
|
-
errors.extend(db_connection.validation.check_field(self
|
373
|
+
errors.extend(db_connection.validation.check_field(self))
|
382
374
|
return errors
|
383
375
|
|
384
376
|
def _check_validators(self):
|
@@ -386,45 +378,18 @@ class Field(RegisterLookupMixin):
|
|
386
378
|
for i, validator in enumerate(self.validators):
|
387
379
|
if not callable(validator):
|
388
380
|
errors.append(
|
389
|
-
|
390
|
-
|
391
|
-
|
381
|
+
PreflightResult(
|
382
|
+
fix=(
|
383
|
+
"All 'validators' must be callable. "
|
392
384
|
f"validators[{i}] ({repr(validator)}) isn't a function or "
|
393
385
|
"instance of a validator class."
|
394
386
|
),
|
395
387
|
obj=self,
|
396
|
-
id="fields.
|
388
|
+
id="fields.invalid_validator",
|
397
389
|
)
|
398
390
|
)
|
399
391
|
return errors
|
400
392
|
|
401
|
-
def _check_deprecation_details(self):
|
402
|
-
if self.system_check_removed_details is not None:
|
403
|
-
return [
|
404
|
-
preflight.Error(
|
405
|
-
self.system_check_removed_details.get(
|
406
|
-
"msg",
|
407
|
-
f"{self.__class__.__name__} has been removed except for support in historical "
|
408
|
-
"migrations.",
|
409
|
-
),
|
410
|
-
hint=self.system_check_removed_details.get("hint"),
|
411
|
-
obj=self,
|
412
|
-
id=self.system_check_removed_details.get("id", "fields.EXXX"),
|
413
|
-
)
|
414
|
-
]
|
415
|
-
elif self.system_check_deprecated_details is not None:
|
416
|
-
return [
|
417
|
-
preflight.Warning(
|
418
|
-
self.system_check_deprecated_details.get(
|
419
|
-
"msg", f"{self.__class__.__name__} has been deprecated."
|
420
|
-
),
|
421
|
-
hint=self.system_check_deprecated_details.get("hint"),
|
422
|
-
obj=self,
|
423
|
-
id=self.system_check_deprecated_details.get("id", "fields.WXXX"),
|
424
|
-
)
|
425
|
-
]
|
426
|
-
return []
|
427
|
-
|
428
393
|
def get_col(self, alias, output_field=None):
|
429
394
|
if alias == self.model._meta.db_table and (
|
430
395
|
output_field is None or output_field == self
|
@@ -985,12 +950,11 @@ class CharField(Field):
|
|
985
950
|
else:
|
986
951
|
return "String (unlimited)"
|
987
952
|
|
988
|
-
def
|
989
|
-
database = kwargs.get("database", False)
|
953
|
+
def preflight(self, **kwargs):
|
990
954
|
return [
|
991
|
-
*super().
|
992
|
-
*self._check_db_collation(
|
993
|
-
*self._check_max_length_attribute(
|
955
|
+
*super().preflight(**kwargs),
|
956
|
+
*self._check_db_collation(),
|
957
|
+
*self._check_max_length_attribute(),
|
994
958
|
]
|
995
959
|
|
996
960
|
def _check_max_length_attribute(self, **kwargs):
|
@@ -1002,10 +966,10 @@ class CharField(Field):
|
|
1002
966
|
):
|
1003
967
|
return []
|
1004
968
|
return [
|
1005
|
-
|
1006
|
-
"CharFields must define a 'max_length' attribute.",
|
969
|
+
PreflightResult(
|
970
|
+
fix="CharFields must define a 'max_length' attribute.",
|
1007
971
|
obj=self,
|
1008
|
-
id="fields.
|
972
|
+
id="fields.charfield_missing_max_length",
|
1009
973
|
)
|
1010
974
|
]
|
1011
975
|
elif (
|
@@ -1014,29 +978,29 @@ class CharField(Field):
|
|
1014
978
|
or self.max_length <= 0
|
1015
979
|
):
|
1016
980
|
return [
|
1017
|
-
|
1018
|
-
"'max_length' must be a positive integer.",
|
981
|
+
PreflightResult(
|
982
|
+
fix="'max_length' must be a positive integer.",
|
1019
983
|
obj=self,
|
1020
|
-
id="fields.
|
984
|
+
id="fields.charfield_invalid_max_length",
|
1021
985
|
)
|
1022
986
|
]
|
1023
987
|
else:
|
1024
988
|
return []
|
1025
989
|
|
1026
|
-
def _check_db_collation(self
|
990
|
+
def _check_db_collation(self):
|
1027
991
|
errors = []
|
1028
|
-
if
|
992
|
+
if not (
|
1029
993
|
self.db_collation is None
|
1030
994
|
or "supports_collation_on_charfield"
|
1031
995
|
in self.model._meta.required_db_features
|
1032
996
|
or db_connection.features.supports_collation_on_charfield
|
1033
997
|
):
|
1034
998
|
errors.append(
|
1035
|
-
|
1036
|
-
f"{db_connection.display_name} does not support a database collation on "
|
999
|
+
PreflightResult(
|
1000
|
+
fix=f"{db_connection.display_name} does not support a database collation on "
|
1037
1001
|
"CharFields.",
|
1038
1002
|
obj=self,
|
1039
|
-
id="fields.
|
1003
|
+
id="fields.db_collation_unsupported",
|
1040
1004
|
),
|
1041
1005
|
)
|
1042
1006
|
return errors
|
@@ -1070,21 +1034,6 @@ class CharField(Field):
|
|
1070
1034
|
return name, path, args, kwargs
|
1071
1035
|
|
1072
1036
|
|
1073
|
-
class CommaSeparatedIntegerField(CharField):
|
1074
|
-
default_validators = [validators.validate_comma_separated_integer_list]
|
1075
|
-
description = "Comma-separated integers"
|
1076
|
-
system_check_removed_details = {
|
1077
|
-
"msg": (
|
1078
|
-
"CommaSeparatedIntegerField is removed except for support in "
|
1079
|
-
"historical migrations."
|
1080
|
-
),
|
1081
|
-
"hint": (
|
1082
|
-
"Use CharField(validators=[validate_comma_separated_integer_list]) instead."
|
1083
|
-
),
|
1084
|
-
"id": "fields.E901",
|
1085
|
-
}
|
1086
|
-
|
1087
|
-
|
1088
1037
|
def _to_naive(value):
|
1089
1038
|
if timezone.is_aware(value):
|
1090
1039
|
value = timezone.make_naive(value, datetime.UTC)
|
@@ -1096,9 +1045,9 @@ def _get_naive_now():
|
|
1096
1045
|
|
1097
1046
|
|
1098
1047
|
class DateTimeCheckMixin:
|
1099
|
-
def
|
1048
|
+
def preflight(self, **kwargs):
|
1100
1049
|
return [
|
1101
|
-
*super().
|
1050
|
+
*super().preflight(**kwargs),
|
1102
1051
|
*self._check_mutually_exclusive_options(),
|
1103
1052
|
*self._check_fix_default_value(),
|
1104
1053
|
]
|
@@ -1117,12 +1066,12 @@ class DateTimeCheckMixin:
|
|
1117
1066
|
].count(True)
|
1118
1067
|
if enabled_options > 1:
|
1119
1068
|
return [
|
1120
|
-
|
1121
|
-
"The options auto_now, auto_now_add, and default "
|
1069
|
+
PreflightResult(
|
1070
|
+
fix="The options auto_now, auto_now_add, and default "
|
1122
1071
|
"are mutually exclusive. Only one of these options "
|
1123
1072
|
"may be present.",
|
1124
1073
|
obj=self,
|
1125
|
-
id="fields.
|
1074
|
+
id="fields.datetime_auto_options_mutually_exclusive",
|
1126
1075
|
)
|
1127
1076
|
]
|
1128
1077
|
else:
|
@@ -1153,16 +1102,15 @@ class DateTimeCheckMixin:
|
|
1153
1102
|
upper = upper.date()
|
1154
1103
|
if lower <= value <= upper:
|
1155
1104
|
return [
|
1156
|
-
|
1157
|
-
"Fixed default value provided."
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
"as default, use `plain.utils.timezone.now`"
|
1163
|
-
),
|
1105
|
+
PreflightResult(
|
1106
|
+
fix="Fixed default value provided. "
|
1107
|
+
"It seems you set a fixed date / time / datetime "
|
1108
|
+
"value as default for this field. This may not be "
|
1109
|
+
"what you want. If you want to have the current date "
|
1110
|
+
"as default, use `plain.utils.timezone.now`",
|
1164
1111
|
obj=self,
|
1165
|
-
id="fields.
|
1112
|
+
id="fields.datetime_naive_default_value",
|
1113
|
+
warning=True,
|
1166
1114
|
)
|
1167
1115
|
]
|
1168
1116
|
return []
|
@@ -1422,15 +1370,15 @@ class DecimalField(Field):
|
|
1422
1370
|
self.max_digits, self.decimal_places = max_digits, decimal_places
|
1423
1371
|
super().__init__(**kwargs)
|
1424
1372
|
|
1425
|
-
def
|
1426
|
-
errors = super().
|
1373
|
+
def preflight(self, **kwargs):
|
1374
|
+
errors = super().preflight(**kwargs)
|
1427
1375
|
|
1428
1376
|
digits_errors = [
|
1429
1377
|
*self._check_decimal_places(),
|
1430
1378
|
*self._check_max_digits(),
|
1431
1379
|
]
|
1432
1380
|
if not digits_errors:
|
1433
|
-
errors.extend(self._check_decimal_places_and_max_digits(
|
1381
|
+
errors.extend(self._check_decimal_places_and_max_digits())
|
1434
1382
|
else:
|
1435
1383
|
errors.extend(digits_errors)
|
1436
1384
|
return errors
|
@@ -1442,18 +1390,18 @@ class DecimalField(Field):
|
|
1442
1390
|
raise ValueError()
|
1443
1391
|
except TypeError:
|
1444
1392
|
return [
|
1445
|
-
|
1446
|
-
"DecimalFields must define a 'decimal_places' attribute.",
|
1393
|
+
PreflightResult(
|
1394
|
+
fix="DecimalFields must define a 'decimal_places' attribute.",
|
1447
1395
|
obj=self,
|
1448
|
-
id="fields.
|
1396
|
+
id="fields.decimalfield_missing_decimal_places",
|
1449
1397
|
)
|
1450
1398
|
]
|
1451
1399
|
except ValueError:
|
1452
1400
|
return [
|
1453
|
-
|
1454
|
-
"'decimal_places' must be a non-negative integer.",
|
1401
|
+
PreflightResult(
|
1402
|
+
fix="'decimal_places' must be a non-negative integer.",
|
1455
1403
|
obj=self,
|
1456
|
-
id="fields.
|
1404
|
+
id="fields.decimalfield_invalid_decimal_places",
|
1457
1405
|
)
|
1458
1406
|
]
|
1459
1407
|
else:
|
@@ -1466,30 +1414,30 @@ class DecimalField(Field):
|
|
1466
1414
|
raise ValueError()
|
1467
1415
|
except TypeError:
|
1468
1416
|
return [
|
1469
|
-
|
1470
|
-
"DecimalFields must define a 'max_digits' attribute.",
|
1417
|
+
PreflightResult(
|
1418
|
+
fix="DecimalFields must define a 'max_digits' attribute.",
|
1471
1419
|
obj=self,
|
1472
|
-
id="fields.
|
1420
|
+
id="fields.decimalfield_missing_max_digits",
|
1473
1421
|
)
|
1474
1422
|
]
|
1475
1423
|
except ValueError:
|
1476
1424
|
return [
|
1477
|
-
|
1478
|
-
"'max_digits' must be a positive integer.",
|
1425
|
+
PreflightResult(
|
1426
|
+
fix="'max_digits' must be a positive integer.",
|
1479
1427
|
obj=self,
|
1480
|
-
id="fields.
|
1428
|
+
id="fields.decimalfield_invalid_max_digits",
|
1481
1429
|
)
|
1482
1430
|
]
|
1483
1431
|
else:
|
1484
1432
|
return []
|
1485
1433
|
|
1486
|
-
def _check_decimal_places_and_max_digits(self
|
1434
|
+
def _check_decimal_places_and_max_digits(self):
|
1487
1435
|
if int(self.decimal_places) > int(self.max_digits):
|
1488
1436
|
return [
|
1489
|
-
|
1490
|
-
"'max_digits' must be greater or equal to 'decimal_places'.",
|
1437
|
+
PreflightResult(
|
1438
|
+
fix="'max_digits' must be greater or equal to 'decimal_places'.",
|
1491
1439
|
obj=self,
|
1492
|
-
id="fields.
|
1440
|
+
id="fields.decimalfield_decimal_places_exceeds_max_digits",
|
1493
1441
|
)
|
1494
1442
|
]
|
1495
1443
|
return []
|
@@ -1662,20 +1610,20 @@ class IntegerField(Field):
|
|
1662
1610
|
}
|
1663
1611
|
description = "Integer"
|
1664
1612
|
|
1665
|
-
def
|
1613
|
+
def preflight(self, **kwargs):
|
1666
1614
|
return [
|
1667
|
-
*super().
|
1615
|
+
*super().preflight(**kwargs),
|
1668
1616
|
*self._check_max_length_warning(),
|
1669
1617
|
]
|
1670
1618
|
|
1671
1619
|
def _check_max_length_warning(self):
|
1672
1620
|
if self.max_length is not None:
|
1673
1621
|
return [
|
1674
|
-
|
1675
|
-
f"'max_length' is ignored when used with {self.__class__.__name__}.",
|
1676
|
-
hint="Remove 'max_length' from field",
|
1622
|
+
PreflightResult(
|
1623
|
+
fix=f"'max_length' is ignored when used with {self.__class__.__name__}. Remove 'max_length' from field.",
|
1677
1624
|
obj=self,
|
1678
|
-
id="fields.
|
1625
|
+
id="fields.max_length_ignored",
|
1626
|
+
warning=True,
|
1679
1627
|
)
|
1680
1628
|
]
|
1681
1629
|
return []
|
@@ -1760,37 +1708,6 @@ class SmallIntegerField(IntegerField):
|
|
1760
1708
|
return "SmallIntegerField"
|
1761
1709
|
|
1762
1710
|
|
1763
|
-
class IPAddressField(Field):
|
1764
|
-
empty_strings_allowed = False
|
1765
|
-
description = "IPv4 address"
|
1766
|
-
system_check_removed_details = {
|
1767
|
-
"msg": (
|
1768
|
-
"IPAddressField has been removed except for support in "
|
1769
|
-
"historical migrations."
|
1770
|
-
),
|
1771
|
-
"hint": "Use GenericIPAddressField instead.",
|
1772
|
-
"id": "fields.E900",
|
1773
|
-
}
|
1774
|
-
|
1775
|
-
def __init__(self, **kwargs):
|
1776
|
-
kwargs["max_length"] = 15
|
1777
|
-
super().__init__(**kwargs)
|
1778
|
-
|
1779
|
-
def deconstruct(self):
|
1780
|
-
name, path, args, kwargs = super().deconstruct()
|
1781
|
-
del kwargs["max_length"]
|
1782
|
-
return name, path, args, kwargs
|
1783
|
-
|
1784
|
-
def get_prep_value(self, value):
|
1785
|
-
value = super().get_prep_value(value)
|
1786
|
-
if value is None:
|
1787
|
-
return None
|
1788
|
-
return str(value)
|
1789
|
-
|
1790
|
-
def get_internal_type(self):
|
1791
|
-
return "IPAddressField"
|
1792
|
-
|
1793
|
-
|
1794
1711
|
class GenericIPAddressField(Field):
|
1795
1712
|
empty_strings_allowed = False
|
1796
1713
|
description = "IP address"
|
@@ -1813,22 +1730,22 @@ class GenericIPAddressField(Field):
|
|
1813
1730
|
kwargs["max_length"] = 39
|
1814
1731
|
super().__init__(**kwargs)
|
1815
1732
|
|
1816
|
-
def
|
1733
|
+
def preflight(self, **kwargs):
|
1817
1734
|
return [
|
1818
|
-
*super().
|
1819
|
-
*self._check_required_and_null_values(
|
1735
|
+
*super().preflight(**kwargs),
|
1736
|
+
*self._check_required_and_null_values(),
|
1820
1737
|
]
|
1821
1738
|
|
1822
|
-
def _check_required_and_null_values(self
|
1739
|
+
def _check_required_and_null_values(self):
|
1823
1740
|
if not getattr(self, "allow_null", False) and not getattr(
|
1824
1741
|
self, "required", True
|
1825
1742
|
):
|
1826
1743
|
return [
|
1827
|
-
|
1828
|
-
"GenericIPAddressFields cannot have required=False if allow_null=False, "
|
1744
|
+
PreflightResult(
|
1745
|
+
fix="GenericIPAddressFields cannot have required=False if allow_null=False, "
|
1829
1746
|
"as blank values are stored as nulls.",
|
1830
1747
|
obj=self,
|
1831
|
-
id="fields.
|
1748
|
+
id="fields.generic_ip_field_null_blank_config",
|
1832
1749
|
)
|
1833
1750
|
]
|
1834
1751
|
return []
|
@@ -1875,32 +1792,6 @@ class GenericIPAddressField(Field):
|
|
1875
1792
|
return str(value)
|
1876
1793
|
|
1877
1794
|
|
1878
|
-
class NullBooleanField(BooleanField):
|
1879
|
-
default_error_messages = {
|
1880
|
-
"invalid": "“%(value)s” value must be either None, True or False.",
|
1881
|
-
"invalid_nullable": "“%(value)s” value must be either None, True or False.",
|
1882
|
-
}
|
1883
|
-
description = "Boolean (Either True, False or None)"
|
1884
|
-
system_check_removed_details = {
|
1885
|
-
"msg": (
|
1886
|
-
"NullBooleanField is removed except for support in historical migrations."
|
1887
|
-
),
|
1888
|
-
"hint": "Use BooleanField(allow_null=True) instead.",
|
1889
|
-
"id": "fields.E903",
|
1890
|
-
}
|
1891
|
-
|
1892
|
-
def __init__(self, **kwargs):
|
1893
|
-
kwargs["allow_null"] = True
|
1894
|
-
kwargs["required"] = False
|
1895
|
-
super().__init__(**kwargs)
|
1896
|
-
|
1897
|
-
def deconstruct(self):
|
1898
|
-
name, path, args, kwargs = super().deconstruct()
|
1899
|
-
del kwargs["allow_null"]
|
1900
|
-
del kwargs["required"]
|
1901
|
-
return name, path, args, kwargs
|
1902
|
-
|
1903
|
-
|
1904
1795
|
class PositiveIntegerRelDbTypeMixin:
|
1905
1796
|
def __init_subclass__(cls, **kwargs):
|
1906
1797
|
super().__init_subclass__(**kwargs)
|
@@ -1957,27 +1848,26 @@ class TextField(Field):
|
|
1957
1848
|
super().__init__(**kwargs)
|
1958
1849
|
self.db_collation = db_collation
|
1959
1850
|
|
1960
|
-
def
|
1961
|
-
database = kwargs.get("database", False)
|
1851
|
+
def preflight(self, **kwargs):
|
1962
1852
|
return [
|
1963
|
-
*super().
|
1964
|
-
*self._check_db_collation(
|
1853
|
+
*super().preflight(**kwargs),
|
1854
|
+
*self._check_db_collation(),
|
1965
1855
|
]
|
1966
1856
|
|
1967
|
-
def _check_db_collation(self
|
1857
|
+
def _check_db_collation(self):
|
1968
1858
|
errors = []
|
1969
|
-
if
|
1859
|
+
if not (
|
1970
1860
|
self.db_collation is None
|
1971
1861
|
or "supports_collation_on_textfield"
|
1972
1862
|
in self.model._meta.required_db_features
|
1973
1863
|
or db_connection.features.supports_collation_on_textfield
|
1974
1864
|
):
|
1975
1865
|
errors.append(
|
1976
|
-
|
1977
|
-
f"{db_connection.display_name} does not support a database collation on "
|
1866
|
+
PreflightResult(
|
1867
|
+
fix=f"{db_connection.display_name} does not support a database collation on "
|
1978
1868
|
"TextFields.",
|
1979
1869
|
obj=self,
|
1980
|
-
id="fields.
|
1870
|
+
id="fields.db_collation_unsupported",
|
1981
1871
|
),
|
1982
1872
|
)
|
1983
1873
|
return errors
|
@@ -2130,17 +2020,17 @@ class BinaryField(Field):
|
|
2130
2020
|
if self.max_length is not None:
|
2131
2021
|
self.validators.append(validators.MaxLengthValidator(self.max_length))
|
2132
2022
|
|
2133
|
-
def
|
2134
|
-
return [*super().
|
2023
|
+
def preflight(self, **kwargs):
|
2024
|
+
return [*super().preflight(**kwargs), *self._check_str_default_value()]
|
2135
2025
|
|
2136
2026
|
def _check_str_default_value(self):
|
2137
2027
|
if self.has_default() and isinstance(self.default, str):
|
2138
2028
|
return [
|
2139
|
-
|
2140
|
-
"BinaryField's default cannot be a string. Use bytes "
|
2029
|
+
PreflightResult(
|
2030
|
+
fix="BinaryField's default cannot be a string. Use bytes "
|
2141
2031
|
"content instead.",
|
2142
2032
|
obj=self,
|
2143
|
-
id="fields.
|
2033
|
+
id="fields.filefield_upload_to_not_callable",
|
2144
2034
|
)
|
2145
2035
|
]
|
2146
2036
|
return []
|
@@ -2236,10 +2126,10 @@ class PrimaryKeyField(BigIntegerField):
|
|
2236
2126
|
self.creation_counter = Field.auto_creation_counter
|
2237
2127
|
Field.auto_creation_counter -= 1
|
2238
2128
|
|
2239
|
-
def
|
2240
|
-
errors = super().
|
2241
|
-
# Remove the
|
2242
|
-
errors = [e for e in errors if e.id != "fields.
|
2129
|
+
def preflight(self, **kwargs):
|
2130
|
+
errors = super().preflight(**kwargs)
|
2131
|
+
# Remove the reserved_field_name_id error for 'id' field name since PrimaryKeyField is allowed to use it
|
2132
|
+
errors = [e for e in errors if e.id != "fields.reserved_field_name_id"]
|
2243
2133
|
return errors
|
2244
2134
|
|
2245
2135
|
def deconstruct(self):
|