diff options
Diffstat (limited to 'django/db/models/fields/__init__.py')
| -rw-r--r-- | django/db/models/fields/__init__.py | 1094 |
1 files changed, 650 insertions, 444 deletions
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 6d6d10a483..313e31b5f5 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -19,7 +19,10 @@ from django.db.models.query_utils import DeferredAttribute, RegisterLookupMixin from django.utils import timezone from django.utils.datastructures import DictWrapper from django.utils.dateparse import ( - parse_date, parse_datetime, parse_duration, parse_time, + parse_date, + parse_datetime, + parse_duration, + parse_time, ) from django.utils.duration import duration_microseconds, duration_string from django.utils.functional import Promise, cached_property @@ -29,14 +32,38 @@ from django.utils.text import capfirst from django.utils.translation import gettext_lazy as _ __all__ = [ - 'AutoField', 'BLANK_CHOICE_DASH', 'BigAutoField', 'BigIntegerField', - 'BinaryField', 'BooleanField', 'CharField', 'CommaSeparatedIntegerField', - 'DateField', 'DateTimeField', 'DecimalField', 'DurationField', - 'EmailField', 'Empty', 'Field', 'FilePathField', 'FloatField', - 'GenericIPAddressField', 'IPAddressField', 'IntegerField', 'NOT_PROVIDED', - 'NullBooleanField', 'PositiveBigIntegerField', 'PositiveIntegerField', - 'PositiveSmallIntegerField', 'SlugField', 'SmallAutoField', - 'SmallIntegerField', 'TextField', 'TimeField', 'URLField', 'UUIDField', + "AutoField", + "BLANK_CHOICE_DASH", + "BigAutoField", + "BigIntegerField", + "BinaryField", + "BooleanField", + "CharField", + "CommaSeparatedIntegerField", + "DateField", + "DateTimeField", + "DecimalField", + "DurationField", + "EmailField", + "Empty", + "Field", + "FilePathField", + "FloatField", + "GenericIPAddressField", + "IPAddressField", + "IntegerField", + "NOT_PROVIDED", + "NullBooleanField", + "PositiveBigIntegerField", + "PositiveIntegerField", + "PositiveSmallIntegerField", + "SlugField", + "SmallAutoField", + "SmallIntegerField", + "TextField", + "TimeField", + "URLField", + "UUIDField", ] @@ -72,6 +99,7 @@ def _load_field(app_label, model_name, field_name): # # getattr(obj, opts.pk.attname) + def _empty(of_cls): new = Empty() new.__class__ = of_cls @@ -98,14 +126,16 @@ class Field(RegisterLookupMixin): auto_creation_counter = -1 default_validators = [] # Default set of validators default_error_messages = { - 'invalid_choice': _('Value %(value)r is not a valid choice.'), - 'null': _('This field cannot be null.'), - 'blank': _('This field cannot be blank.'), - 'unique': _('%(model_name)s with this %(field_label)s already exists.'), + "invalid_choice": _("Value %(value)r is not a valid choice."), + "null": _("This field cannot be null."), + "blank": _("This field cannot be blank."), + "unique": _("%(model_name)s with this %(field_label)s already exists."), # Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. # Eg: "Title must be unique for pub_date year" - 'unique_for_date': _("%(field_label)s must be unique for " - "%(date_field_label)s %(lookup_type)s."), + "unique_for_date": _( + "%(field_label)s must be unique for " + "%(date_field_label)s %(lookup_type)s." + ), } system_check_deprecated_details = None system_check_removed_details = None @@ -123,18 +153,37 @@ class Field(RegisterLookupMixin): # Generic field type description, usually overridden by subclasses def _description(self): - return _('Field of type: %(field_type)s') % { - 'field_type': self.__class__.__name__ + return _("Field of type: %(field_type)s") % { + "field_type": self.__class__.__name__ } + description = property(_description) - def __init__(self, verbose_name=None, name=None, primary_key=False, - max_length=None, unique=False, blank=False, null=False, - db_index=False, rel=None, default=NOT_PROVIDED, editable=True, - serialize=True, unique_for_date=None, unique_for_month=None, - unique_for_year=None, choices=None, help_text='', db_column=None, - db_tablespace=None, auto_created=False, validators=(), - error_messages=None): + def __init__( + self, + verbose_name=None, + name=None, + primary_key=False, + max_length=None, + unique=False, + blank=False, + null=False, + db_index=False, + rel=None, + default=NOT_PROVIDED, + editable=True, + serialize=True, + unique_for_date=None, + unique_for_month=None, + unique_for_year=None, + choices=None, + help_text="", + db_column=None, + db_tablespace=None, + auto_created=False, + validators=(), + error_messages=None, + ): self.name = name self.verbose_name = verbose_name # May be set by set_attributes_from_name self._verbose_name = verbose_name # Store original for deconstruction @@ -170,7 +219,7 @@ class Field(RegisterLookupMixin): messages = {} for c in reversed(self.__class__.__mro__): - messages.update(getattr(c, 'default_error_messages', {})) + messages.update(getattr(c, "default_error_messages", {})) messages.update(error_messages or {}) self._error_messages = error_messages # Store for deconstruction later self.error_messages = messages @@ -180,18 +229,18 @@ class Field(RegisterLookupMixin): Return "app_label.model_label.field_name" for fields attached to models. """ - if not hasattr(self, 'model'): + if not hasattr(self, "model"): return super().__str__() model = self.model - return '%s.%s' % (model._meta.label, self.name) + return "%s.%s" % (model._meta.label, self.name) def __repr__(self): """Display the module, class, and name of the field.""" - path = '%s.%s' % (self.__class__.__module__, self.__class__.__qualname__) - name = getattr(self, 'name', None) + path = "%s.%s" % (self.__class__.__module__, self.__class__.__qualname__) + name = getattr(self, "name", None) if name is not None: - return '<%s: %s>' % (path, name) - return '<%s>' % path + return "<%s: %s>" % (path, name) + return "<%s>" % path def check(self, **kwargs): return [ @@ -209,12 +258,12 @@ class Field(RegisterLookupMixin): Check if field name is valid, i.e. 1) does not end with an underscore, 2) does not contain "__" and 3) is not "pk". """ - if self.name.endswith('_'): + if self.name.endswith("_"): return [ checks.Error( - 'Field names must not end with an underscore.', + "Field names must not end with an underscore.", obj=self, - id='fields.E001', + id="fields.E001", ) ] elif LOOKUP_SEP in self.name: @@ -222,15 +271,15 @@ class Field(RegisterLookupMixin): checks.Error( 'Field names must not contain "%s".' % LOOKUP_SEP, obj=self, - id='fields.E002', + id="fields.E002", ) ] - elif self.name == 'pk': + elif self.name == "pk": return [ checks.Error( "'pk' is a reserved word that cannot be used as a field name.", obj=self, - id='fields.E003', + id="fields.E003", ) ] else: @@ -249,7 +298,7 @@ class Field(RegisterLookupMixin): checks.Error( "'choices' must be an iterable (e.g., a list or tuple).", obj=self, - id='fields.E004', + id="fields.E004", ) ] @@ -268,14 +317,22 @@ class Field(RegisterLookupMixin): ): break if self.max_length is not None and group_choices: - choice_max_length = max([ - choice_max_length, - *(len(value) for value, _ in group_choices if isinstance(value, str)), - ]) + choice_max_length = max( + [ + choice_max_length, + *( + len(value) + for value, _ in group_choices + if isinstance(value, str) + ), + ] + ) except (TypeError, ValueError): # No groups, choices in the form [value, display] value, human_name = group_name, group_choices - if not self._choices_is_value(value) or not self._choices_is_value(human_name): + if not self._choices_is_value(value) or not self._choices_is_value( + human_name + ): break if self.max_length is not None and isinstance(value, str): choice_max_length = max(choice_max_length, len(value)) @@ -290,7 +347,7 @@ class Field(RegisterLookupMixin): "'max_length' is too small to fit the longest value " "in 'choices' (%d characters)." % choice_max_length, obj=self, - id='fields.E009', + id="fields.E009", ), ] return [] @@ -300,7 +357,7 @@ class Field(RegisterLookupMixin): "'choices' must be an iterable containing " "(actual value, human readable name) tuples.", obj=self, - id='fields.E005', + id="fields.E005", ) ] @@ -310,25 +367,30 @@ class Field(RegisterLookupMixin): checks.Error( "'db_index' must be None, True or False.", obj=self, - id='fields.E006', + id="fields.E006", ) ] else: return [] def _check_null_allowed_for_primary_keys(self): - if (self.primary_key and self.null and - not connection.features.interprets_empty_strings_as_nulls): + if ( + self.primary_key + and self.null + and not connection.features.interprets_empty_strings_as_nulls + ): # We cannot reliably check this for backends like Oracle which # consider NULL and '' to be equal (and thus set up # character-based fields a little differently). return [ checks.Error( - 'Primary keys must not have null=True.', - hint=('Set null=False on the field, or ' - 'remove primary_key=True argument.'), + "Primary keys must not have null=True.", + hint=( + "Set null=False on the field, or " + "remove primary_key=True argument." + ), obj=self, - id='fields.E007', + id="fields.E007", ) ] else: @@ -340,7 +402,9 @@ class Field(RegisterLookupMixin): app_label = self.model._meta.app_label errors = [] for alias in databases: - if router.allow_migrate(alias, app_label, model_name=self.model._meta.model_name): + if router.allow_migrate( + alias, app_label, model_name=self.model._meta.model_name + ): errors.extend(connections[alias].validation.check_field(self, **kwargs)) return errors @@ -354,11 +418,12 @@ class Field(RegisterLookupMixin): hint=( "validators[{i}] ({repr}) isn't a function or " "instance of a validator class.".format( - i=i, repr=repr(validator), + i=i, + repr=repr(validator), ) ), obj=self, - id='fields.E008', + id="fields.E008", ) ) return errors @@ -368,41 +433,41 @@ class Field(RegisterLookupMixin): return [ checks.Error( self.system_check_removed_details.get( - 'msg', - '%s has been removed except for support in historical ' - 'migrations.' % self.__class__.__name__ + "msg", + "%s has been removed except for support in historical " + "migrations." % self.__class__.__name__, ), - hint=self.system_check_removed_details.get('hint'), + hint=self.system_check_removed_details.get("hint"), obj=self, - id=self.system_check_removed_details.get('id', 'fields.EXXX'), + id=self.system_check_removed_details.get("id", "fields.EXXX"), ) ] elif self.system_check_deprecated_details is not None: return [ checks.Warning( self.system_check_deprecated_details.get( - 'msg', - '%s has been deprecated.' % self.__class__.__name__ + "msg", "%s has been deprecated." % self.__class__.__name__ ), - hint=self.system_check_deprecated_details.get('hint'), + hint=self.system_check_deprecated_details.get("hint"), obj=self, - id=self.system_check_deprecated_details.get('id', 'fields.WXXX'), + id=self.system_check_deprecated_details.get("id", "fields.WXXX"), ) ] return [] def get_col(self, alias, output_field=None): - if ( - alias == self.model._meta.db_table and - (output_field is None or output_field == self) + if alias == self.model._meta.db_table and ( + output_field is None or output_field == self ): return self.cached_col from django.db.models.expressions import Col + return Col(alias, self, output_field) @cached_property def cached_col(self): from django.db.models.expressions import Col + return Col(self.model._meta.db_table, self) def select_format(self, compiler, sql, params): @@ -462,7 +527,7 @@ class Field(RegisterLookupMixin): "unique_for_month": None, "unique_for_year": None, "choices": None, - "help_text": '', + "help_text": "", "db_column": None, "db_tablespace": None, "auto_created": False, @@ -495,8 +560,8 @@ class Field(RegisterLookupMixin): path = path.replace("django.db.models.fields.related", "django.db.models") elif path.startswith("django.db.models.fields.files"): path = path.replace("django.db.models.fields.files", "django.db.models") - elif path.startswith('django.db.models.fields.json'): - path = path.replace('django.db.models.fields.json', 'django.db.models') + elif path.startswith("django.db.models.fields.json"): + path = path.replace("django.db.models.fields.json", "django.db.models") elif path.startswith("django.db.models.fields.proxy"): path = path.replace("django.db.models.fields.proxy", "django.db.models") elif path.startswith("django.db.models.fields"): @@ -515,10 +580,9 @@ class Field(RegisterLookupMixin): def __eq__(self, other): # Needed for @total_ordering if isinstance(other, Field): - return ( - self.creation_counter == other.creation_counter and - getattr(self, 'model', None) == getattr(other, 'model', None) - ) + return self.creation_counter == other.creation_counter and getattr( + self, "model", None + ) == getattr(other, "model", None) return NotImplemented def __lt__(self, other): @@ -526,17 +590,18 @@ class Field(RegisterLookupMixin): # Order by creation_counter first for backward compatibility. if isinstance(other, Field): if ( - self.creation_counter != other.creation_counter or - not hasattr(self, 'model') and not hasattr(other, 'model') + self.creation_counter != other.creation_counter + or not hasattr(self, "model") + and not hasattr(other, "model") ): return self.creation_counter < other.creation_counter - elif hasattr(self, 'model') != hasattr(other, 'model'): - return not hasattr(self, 'model') # Order no-model fields first + elif hasattr(self, "model") != hasattr(other, "model"): + return not hasattr(self, "model") # Order no-model fields first else: # creation_counter's are equal, compare only models. - return ( - (self.model._meta.app_label, self.model._meta.model_name) < - (other.model._meta.app_label, other.model._meta.model_name) + return (self.model._meta.app_label, self.model._meta.model_name) < ( + other.model._meta.app_label, + other.model._meta.model_name, ) return NotImplemented @@ -549,7 +614,7 @@ class Field(RegisterLookupMixin): obj = copy.copy(self) if self.remote_field: obj.remote_field = copy.copy(self.remote_field) - if hasattr(self.remote_field, 'field') and self.remote_field.field is self: + if hasattr(self.remote_field, "field") and self.remote_field.field is self: obj.remote_field.field = obj memodict[id(self)] = obj return obj @@ -568,7 +633,7 @@ class Field(RegisterLookupMixin): not a new copy of that field. So, use the app registry to load the model and then the field back. """ - if not hasattr(self, 'model'): + if not hasattr(self, "model"): # Fields are sometimes used without attaching them to models (for # example in aggregation). In this case give back a plain field # instance. The code below will create a new empty instance of @@ -577,10 +642,13 @@ class Field(RegisterLookupMixin): state = self.__dict__.copy() # The _get_default cached_property can't be pickled due to lambda # usage. - state.pop('_get_default', None) + state.pop("_get_default", None) return _empty, (self.__class__,), state - return _load_field, (self.model._meta.app_label, self.model._meta.object_name, - self.name) + return _load_field, ( + self.model._meta.app_label, + self.model._meta.object_name, + self.name, + ) def get_pk_value_on_save(self, instance): """ @@ -618,7 +686,7 @@ class Field(RegisterLookupMixin): try: v(value) except exceptions.ValidationError as e: - if hasattr(e, 'code') and e.code in self.error_messages: + if hasattr(e, "code") and e.code in self.error_messages: e.message = self.error_messages[e.code] errors.extend(e.error_list) @@ -645,16 +713,16 @@ class Field(RegisterLookupMixin): elif value == option_key: return raise exceptions.ValidationError( - self.error_messages['invalid_choice'], - code='invalid_choice', - params={'value': value}, + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": value}, ) if value is None and not self.null: - raise exceptions.ValidationError(self.error_messages['null'], code='null') + raise exceptions.ValidationError(self.error_messages["null"], code="null") if not self.blank and value in self.empty_values: - raise exceptions.ValidationError(self.error_messages['blank'], code='blank') + raise exceptions.ValidationError(self.error_messages["blank"], code="blank") def clean(self, value, model_instance): """ @@ -668,7 +736,7 @@ class Field(RegisterLookupMixin): return value def db_type_parameters(self, connection): - return DictWrapper(self.__dict__, connection.ops.quote_name, 'qn_') + return DictWrapper(self.__dict__, connection.ops.quote_name, "qn_") def db_check(self, connection): """ @@ -678,7 +746,9 @@ class Field(RegisterLookupMixin): """ data = self.db_type_parameters(connection) try: - return connection.data_type_check_constraints[self.get_internal_type()] % data + return ( + connection.data_type_check_constraints[self.get_internal_type()] % data + ) except KeyError: return None @@ -740,7 +810,7 @@ class Field(RegisterLookupMixin): return connection.data_types_suffix.get(self.get_internal_type()) def get_db_converters(self, connection): - if hasattr(self, 'from_db_value'): + if hasattr(self, "from_db_value"): return [self.from_db_value] return [] @@ -765,7 +835,7 @@ class Field(RegisterLookupMixin): self.attname, self.column = self.get_attname_column() self.concrete = self.column is not None if self.verbose_name is None and self.name: - self.verbose_name = self.name.replace('_', ' ') + self.verbose_name = self.name.replace("_", " ") def contribute_to_class(self, cls, name, private_only=False): """ @@ -784,10 +854,10 @@ class Field(RegisterLookupMixin): # this class, but don't check methods derived from inheritance, to # allow overriding inherited choices. For more complex inheritance # structures users should override contribute_to_class(). - if 'get_%s_display' % self.name not in cls.__dict__: + if "get_%s_display" % self.name not in cls.__dict__: setattr( cls, - 'get_%s_display' % self.name, + "get_%s_display" % self.name, partialmethod(cls._get_FIELD_display, field=self), ) @@ -848,11 +918,21 @@ class Field(RegisterLookupMixin): return self.default return lambda: self.default - if not self.empty_strings_allowed or self.null and not connection.features.interprets_empty_strings_as_nulls: + if ( + not self.empty_strings_allowed + or self.null + and not connection.features.interprets_empty_strings_as_nulls + ): return return_None return str # return empty string - def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH, limit_choices_to=None, ordering=()): + def get_choices( + self, + include_blank=True, + blank_choice=BLANK_CHOICE_DASH, + limit_choices_to=None, + ordering=(), + ): """ Return choices with a default blank choices included, for use as <select> choices for this field. @@ -860,7 +940,9 @@ class Field(RegisterLookupMixin): if self.choices is not None: choices = list(self.choices) if include_blank: - blank_defined = any(choice in ('', None) for choice, _ in self.flatchoices) + blank_defined = any( + choice in ("", None) for choice, _ in self.flatchoices + ) if not blank_defined: choices = blank_choice + choices return choices @@ -868,8 +950,8 @@ class Field(RegisterLookupMixin): limit_choices_to = limit_choices_to or self.get_limit_choices_to() choice_func = operator.attrgetter( self.remote_field.get_related_field().attname - if hasattr(self.remote_field, 'get_related_field') - else 'pk' + if hasattr(self.remote_field, "get_related_field") + else "pk" ) qs = rel_model._default_manager.complex_filter(limit_choices_to) if ordering: @@ -896,6 +978,7 @@ class Field(RegisterLookupMixin): else: flat.append((choice, value)) return flat + flatchoices = property(_get_flatchoices) def save_form_data(self, instance, data): @@ -904,24 +987,25 @@ class Field(RegisterLookupMixin): def formfield(self, form_class=None, choices_form_class=None, **kwargs): """Return a django.forms.Field instance for this field.""" defaults = { - 'required': not self.blank, - 'label': capfirst(self.verbose_name), - 'help_text': self.help_text, + "required": not self.blank, + "label": capfirst(self.verbose_name), + "help_text": self.help_text, } if self.has_default(): if callable(self.default): - defaults['initial'] = self.default - defaults['show_hidden_initial'] = True + defaults["initial"] = self.default + defaults["show_hidden_initial"] = True else: - defaults['initial'] = self.get_default() + defaults["initial"] = self.get_default() if self.choices is not None: # Fields with choices get special treatment. - include_blank = (self.blank or - not (self.has_default() or 'initial' in kwargs)) - defaults['choices'] = self.get_choices(include_blank=include_blank) - defaults['coerce'] = self.to_python + include_blank = self.blank or not ( + self.has_default() or "initial" in kwargs + ) + defaults["choices"] = self.get_choices(include_blank=include_blank) + defaults["coerce"] = self.to_python if self.null: - defaults['empty_value'] = None + defaults["empty_value"] = None if choices_form_class is not None: form_class = choices_form_class else: @@ -930,9 +1014,19 @@ class Field(RegisterLookupMixin): # max_value) don't apply for choice fields, so be sure to only pass # the values that TypedChoiceField will understand. for k in list(kwargs): - if k not in ('coerce', 'empty_value', 'choices', 'required', - 'widget', 'label', 'initial', 'help_text', - 'error_messages', 'show_hidden_initial', 'disabled'): + if k not in ( + "coerce", + "empty_value", + "choices", + "required", + "widget", + "label", + "initial", + "help_text", + "error_messages", + "show_hidden_initial", + "disabled", + ): del kwargs[k] defaults.update(kwargs) if form_class is None: @@ -947,8 +1041,8 @@ class Field(RegisterLookupMixin): class BooleanField(Field): empty_strings_allowed = False default_error_messages = { - 'invalid': _('“%(value)s” value must be either True or False.'), - 'invalid_nullable': _('“%(value)s” value must be either True, False, or None.'), + "invalid": _("“%(value)s” value must be either True or False."), + "invalid_nullable": _("“%(value)s” value must be either True, False, or None."), } description = _("Boolean (Either True or False)") @@ -961,14 +1055,14 @@ class BooleanField(Field): if value in (True, False): # 1/0 are equal to True/False. bool() converts former to latter. return bool(value) - if value in ('t', 'True', '1'): + if value in ("t", "True", "1"): return True - if value in ('f', 'False', '0'): + if value in ("f", "False", "0"): return False raise exceptions.ValidationError( - self.error_messages['invalid_nullable' if self.null else 'invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid_nullable" if self.null else "invalid"], + code="invalid", + params={"value": value}, ) def get_prep_value(self, value): @@ -979,14 +1073,14 @@ class BooleanField(Field): def formfield(self, **kwargs): if self.choices is not None: - include_blank = not (self.has_default() or 'initial' in kwargs) - defaults = {'choices': self.get_choices(include_blank=include_blank)} + include_blank = not (self.has_default() or "initial" in kwargs) + defaults = {"choices": self.get_choices(include_blank=include_blank)} else: form_class = forms.NullBooleanField if self.null else forms.BooleanField # In HTML checkboxes, 'required' means "must be checked" which is # different from the choices case ("must select some value"). # required=False allows unchecked checkboxes. - defaults = {'form_class': form_class, 'required': False} + defaults = {"form_class": form_class, "required": False} return super().formfield(**{**defaults, **kwargs}) def select_format(self, compiler, sql, params): @@ -994,8 +1088,8 @@ class BooleanField(Field): # Filters that match everything are handled as empty strings in the # WHERE clause, but in SELECT or GROUP BY list they must use a # predicate that's always True. - if sql == '': - sql = '1' + if sql == "": + sql = "1" return sql, params @@ -1009,7 +1103,7 @@ class CharField(Field): self.validators.append(validators.MaxLengthValidator(self.max_length)) def check(self, **kwargs): - databases = kwargs.get('databases') or [] + databases = kwargs.get("databases") or [] return [ *super().check(**kwargs), *self._check_db_collation(databases), @@ -1022,16 +1116,19 @@ class CharField(Field): checks.Error( "CharFields must define a 'max_length' attribute.", obj=self, - id='fields.E120', + id="fields.E120", ) ] - elif (not isinstance(self.max_length, int) or isinstance(self.max_length, bool) or - self.max_length <= 0): + elif ( + not isinstance(self.max_length, int) + or isinstance(self.max_length, bool) + or self.max_length <= 0 + ): return [ checks.Error( "'max_length' must be a positive integer.", obj=self, - id='fields.E121', + id="fields.E121", ) ] else: @@ -1044,16 +1141,17 @@ class CharField(Field): continue connection = connections[db] if not ( - self.db_collation is None or - 'supports_collation_on_charfield' in self.model._meta.required_db_features or - connection.features.supports_collation_on_charfield + self.db_collation is None + or "supports_collation_on_charfield" + in self.model._meta.required_db_features + or connection.features.supports_collation_on_charfield ): errors.append( checks.Error( - '%s does not support a database collation on ' - 'CharFields.' % connection.display_name, + "%s does not support a database collation on " + "CharFields." % connection.display_name, obj=self, - id='fields.E190', + id="fields.E190", ), ) return errors @@ -1079,17 +1177,17 @@ class CharField(Field): # Passing max_length to forms.CharField means that the value's length # will be validated twice. This is considered acceptable since we want # the value in the form field (to pass into widget for example). - defaults = {'max_length': self.max_length} + defaults = {"max_length": self.max_length} # TODO: Handle multiple backends with different feature flags. if self.null and not connection.features.interprets_empty_strings_as_nulls: - defaults['empty_value'] = None + defaults["empty_value"] = None defaults.update(kwargs) return super().formfield(**defaults) def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.db_collation: - kwargs['db_collation'] = self.db_collation + kwargs["db_collation"] = self.db_collation return name, path, args, kwargs @@ -1097,15 +1195,15 @@ class CommaSeparatedIntegerField(CharField): default_validators = [validators.validate_comma_separated_integer_list] description = _("Comma-separated integers") system_check_removed_details = { - 'msg': ( - 'CommaSeparatedIntegerField is removed except for support in ' - 'historical migrations.' + "msg": ( + "CommaSeparatedIntegerField is removed except for support in " + "historical migrations." ), - 'hint': ( - 'Use CharField(validators=[validate_comma_separated_integer_list]) ' - 'instead.' + "hint": ( + "Use CharField(validators=[validate_comma_separated_integer_list]) " + "instead." ), - 'id': 'fields.E901', + "id": "fields.E901", } @@ -1120,7 +1218,6 @@ def _get_naive_now(): class DateTimeCheckMixin: - def check(self, **kwargs): return [ *super().check(**kwargs), @@ -1132,8 +1229,14 @@ class DateTimeCheckMixin: # auto_now, auto_now_add, and default are mutually exclusive # options. The use of more than one of these options together # will trigger an Error - mutually_exclusive_options = [self.auto_now_add, self.auto_now, self.has_default()] - enabled_options = [option not in (None, False) for option in mutually_exclusive_options].count(True) + mutually_exclusive_options = [ + self.auto_now_add, + self.auto_now, + self.has_default(), + ] + enabled_options = [ + option not in (None, False) for option in mutually_exclusive_options + ].count(True) if enabled_options > 1: return [ checks.Error( @@ -1141,7 +1244,7 @@ class DateTimeCheckMixin: "are mutually exclusive. Only one of these options " "may be present.", obj=self, - id='fields.E160', + id="fields.E160", ) ] else: @@ -1173,15 +1276,15 @@ class DateTimeCheckMixin: if lower <= value <= upper: return [ checks.Warning( - 'Fixed default value provided.', + "Fixed default value provided.", hint=( - 'It seems you set a fixed date / time / datetime ' - 'value as default for this field. This may not be ' - 'what you want. If you want to have the current date ' - 'as default, use `django.utils.timezone.now`' + "It seems you set a fixed date / time / datetime " + "value as default for this field. This may not be " + "what you want. If you want to have the current date " + "as default, use `django.utils.timezone.now`" ), obj=self, - id='fields.W161', + id="fields.W161", ) ] return [] @@ -1190,19 +1293,24 @@ class DateTimeCheckMixin: class DateField(DateTimeCheckMixin, Field): empty_strings_allowed = False default_error_messages = { - 'invalid': _('“%(value)s” value has an invalid date format. It must be ' - 'in YYYY-MM-DD format.'), - 'invalid_date': _('“%(value)s” value has the correct format (YYYY-MM-DD) ' - 'but it is an invalid date.'), + "invalid": _( + "“%(value)s” value has an invalid date format. It must be " + "in YYYY-MM-DD format." + ), + "invalid_date": _( + "“%(value)s” value has the correct format (YYYY-MM-DD) " + "but it is an invalid date." + ), } description = _("Date (without time)") - def __init__(self, verbose_name=None, name=None, auto_now=False, - auto_now_add=False, **kwargs): + def __init__( + self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs + ): self.auto_now, self.auto_now_add = auto_now, auto_now_add if auto_now or auto_now_add: - kwargs['editable'] = False - kwargs['blank'] = True + kwargs["editable"] = False + kwargs["blank"] = True super().__init__(verbose_name, name, **kwargs) def _check_fix_default_value(self): @@ -1227,12 +1335,12 @@ class DateField(DateTimeCheckMixin, Field): def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.auto_now: - kwargs['auto_now'] = True + kwargs["auto_now"] = True if self.auto_now_add: - kwargs['auto_now_add'] = True + kwargs["auto_now_add"] = True if self.auto_now or self.auto_now_add: - del kwargs['editable'] - del kwargs['blank'] + del kwargs["editable"] + del kwargs["blank"] return name, path, args, kwargs def get_internal_type(self): @@ -1257,15 +1365,15 @@ class DateField(DateTimeCheckMixin, Field): return parsed except ValueError: raise exceptions.ValidationError( - self.error_messages['invalid_date'], - code='invalid_date', - params={'value': value}, + self.error_messages["invalid_date"], + code="invalid_date", + params={"value": value}, ) raise exceptions.ValidationError( - self.error_messages['invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid"], + code="invalid", + params={"value": value}, ) def pre_save(self, model_instance, add): @@ -1280,12 +1388,18 @@ class DateField(DateTimeCheckMixin, Field): super().contribute_to_class(cls, name, **kwargs) if not self.null: setattr( - cls, 'get_next_by_%s' % self.name, - partialmethod(cls._get_next_or_previous_by_FIELD, field=self, is_next=True) + cls, + "get_next_by_%s" % self.name, + partialmethod( + cls._get_next_or_previous_by_FIELD, field=self, is_next=True + ), ) setattr( - cls, 'get_previous_by_%s' % self.name, - partialmethod(cls._get_next_or_previous_by_FIELD, field=self, is_next=False) + cls, + "get_previous_by_%s" % self.name, + partialmethod( + cls._get_next_or_previous_by_FIELD, field=self, is_next=False + ), ) def get_prep_value(self, value): @@ -1300,25 +1414,33 @@ class DateField(DateTimeCheckMixin, Field): def value_to_string(self, obj): val = self.value_from_object(obj) - return '' if val is None else val.isoformat() + return "" if val is None else val.isoformat() def formfield(self, **kwargs): - return super().formfield(**{ - 'form_class': forms.DateField, - **kwargs, - }) + return super().formfield( + **{ + "form_class": forms.DateField, + **kwargs, + } + ) class DateTimeField(DateField): empty_strings_allowed = False default_error_messages = { - 'invalid': _('“%(value)s” value has an invalid format. It must be in ' - 'YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.'), - 'invalid_date': _("“%(value)s” value has the correct format " - "(YYYY-MM-DD) but it is an invalid date."), - 'invalid_datetime': _('“%(value)s” value has the correct format ' - '(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) ' - 'but it is an invalid date/time.'), + "invalid": _( + "“%(value)s” value has an invalid format. It must be in " + "YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format." + ), + "invalid_date": _( + "“%(value)s” value has the correct format " + "(YYYY-MM-DD) but it is an invalid date." + ), + "invalid_datetime": _( + "“%(value)s” value has the correct format " + "(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) " + "but it is an invalid date/time." + ), } description = _("Date (with time)") @@ -1353,10 +1475,12 @@ class DateTimeField(DateField): # local time. This won't work during DST change, but we can't # do much about it, so we let the exceptions percolate up the # call stack. - warnings.warn("DateTimeField %s.%s received a naive datetime " - "(%s) while time zone support is active." % - (self.model.__name__, self.name, value), - RuntimeWarning) + warnings.warn( + "DateTimeField %s.%s received a naive datetime " + "(%s) while time zone support is active." + % (self.model.__name__, self.name, value), + RuntimeWarning, + ) default_timezone = timezone.get_default_timezone() value = timezone.make_aware(value, default_timezone) return value @@ -1367,9 +1491,9 @@ class DateTimeField(DateField): return parsed except ValueError: raise exceptions.ValidationError( - self.error_messages['invalid_datetime'], - code='invalid_datetime', - params={'value': value}, + self.error_messages["invalid_datetime"], + code="invalid_datetime", + params={"value": value}, ) try: @@ -1378,15 +1502,15 @@ class DateTimeField(DateField): return datetime.datetime(parsed.year, parsed.month, parsed.day) except ValueError: raise exceptions.ValidationError( - self.error_messages['invalid_date'], - code='invalid_date', - params={'value': value}, + self.error_messages["invalid_date"], + code="invalid_date", + params={"value": value}, ) raise exceptions.ValidationError( - self.error_messages['invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid"], + code="invalid", + params={"value": value}, ) def pre_save(self, model_instance, add): @@ -1408,13 +1532,14 @@ class DateTimeField(DateField): # time. This won't work during DST change, but we can't do much # about it, so we let the exceptions percolate up the call stack. try: - name = '%s.%s' % (self.model.__name__, self.name) + name = "%s.%s" % (self.model.__name__, self.name) except AttributeError: - name = '(unbound)' - warnings.warn("DateTimeField %s received a naive datetime (%s)" - " while time zone support is active." % - (name, value), - RuntimeWarning) + name = "(unbound)" + warnings.warn( + "DateTimeField %s received a naive datetime (%s)" + " while time zone support is active." % (name, value), + RuntimeWarning, + ) default_timezone = timezone.get_default_timezone() value = timezone.make_aware(value, default_timezone) return value @@ -1427,24 +1552,32 @@ class DateTimeField(DateField): def value_to_string(self, obj): val = self.value_from_object(obj) - return '' if val is None else val.isoformat() + return "" if val is None else val.isoformat() def formfield(self, **kwargs): - return super().formfield(**{ - 'form_class': forms.DateTimeField, - **kwargs, - }) + return super().formfield( + **{ + "form_class": forms.DateTimeField, + **kwargs, + } + ) class DecimalField(Field): empty_strings_allowed = False default_error_messages = { - 'invalid': _('“%(value)s” value must be a decimal number.'), + "invalid": _("“%(value)s” value must be a decimal number."), } description = _("Decimal number") - def __init__(self, verbose_name=None, name=None, max_digits=None, - decimal_places=None, **kwargs): + def __init__( + self, + verbose_name=None, + name=None, + max_digits=None, + decimal_places=None, + **kwargs, + ): self.max_digits, self.decimal_places = max_digits, decimal_places super().__init__(verbose_name, name, **kwargs) @@ -1471,7 +1604,7 @@ class DecimalField(Field): checks.Error( "DecimalFields must define a 'decimal_places' attribute.", obj=self, - id='fields.E130', + id="fields.E130", ) ] except ValueError: @@ -1479,7 +1612,7 @@ class DecimalField(Field): checks.Error( "'decimal_places' must be a non-negative integer.", obj=self, - id='fields.E131', + id="fields.E131", ) ] else: @@ -1495,7 +1628,7 @@ class DecimalField(Field): checks.Error( "DecimalFields must define a 'max_digits' attribute.", obj=self, - id='fields.E132', + id="fields.E132", ) ] except ValueError: @@ -1503,7 +1636,7 @@ class DecimalField(Field): checks.Error( "'max_digits' must be a positive integer.", obj=self, - id='fields.E133', + id="fields.E133", ) ] else: @@ -1515,7 +1648,7 @@ class DecimalField(Field): checks.Error( "'max_digits' must be greater or equal to 'decimal_places'.", obj=self, - id='fields.E134', + id="fields.E134", ) ] return [] @@ -1533,9 +1666,9 @@ class DecimalField(Field): def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.max_digits is not None: - kwargs['max_digits'] = self.max_digits + kwargs["max_digits"] = self.max_digits if self.decimal_places is not None: - kwargs['decimal_places'] = self.decimal_places + kwargs["decimal_places"] = self.decimal_places return name, path, args, kwargs def get_internal_type(self): @@ -1547,34 +1680,38 @@ class DecimalField(Field): if isinstance(value, float): if math.isnan(value): raise exceptions.ValidationError( - self.error_messages['invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid"], + code="invalid", + params={"value": value}, ) return self.context.create_decimal_from_float(value) try: return decimal.Decimal(value) except (decimal.InvalidOperation, TypeError, ValueError): raise exceptions.ValidationError( - self.error_messages['invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid"], + code="invalid", + params={"value": value}, ) def get_db_prep_save(self, value, connection): - return connection.ops.adapt_decimalfield_value(self.to_python(value), self.max_digits, self.decimal_places) + return connection.ops.adapt_decimalfield_value( + self.to_python(value), self.max_digits, self.decimal_places + ) def get_prep_value(self, value): value = super().get_prep_value(value) return self.to_python(value) def formfield(self, **kwargs): - return super().formfield(**{ - 'max_digits': self.max_digits, - 'decimal_places': self.decimal_places, - 'form_class': forms.DecimalField, - **kwargs, - }) + return super().formfield( + **{ + "max_digits": self.max_digits, + "decimal_places": self.decimal_places, + "form_class": forms.DecimalField, + **kwargs, + } + ) class DurationField(Field): @@ -1584,10 +1721,13 @@ class DurationField(Field): Use interval on PostgreSQL, INTERVAL DAY TO SECOND on Oracle, and bigint of microseconds on other databases. """ + empty_strings_allowed = False default_error_messages = { - 'invalid': _('“%(value)s” value has an invalid format. It must be in ' - '[DD] [[HH:]MM:]ss[.uuuuuu] format.') + "invalid": _( + "“%(value)s” value has an invalid format. It must be in " + "[DD] [[HH:]MM:]ss[.uuuuuu] format." + ) } description = _("Duration") @@ -1608,9 +1748,9 @@ class DurationField(Field): return parsed raise exceptions.ValidationError( - self.error_messages['invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid"], + code="invalid", + params={"value": value}, ) def get_db_prep_value(self, value, connection, prepared=False): @@ -1628,13 +1768,15 @@ class DurationField(Field): def value_to_string(self, obj): val = self.value_from_object(obj) - return '' if val is None else duration_string(val) + return "" if val is None else duration_string(val) def formfield(self, **kwargs): - return super().formfield(**{ - 'form_class': forms.DurationField, - **kwargs, - }) + return super().formfield( + **{ + "form_class": forms.DurationField, + **kwargs, + } + ) class EmailField(CharField): @@ -1643,7 +1785,7 @@ class EmailField(CharField): def __init__(self, *args, **kwargs): # max_length=254 to be compliant with RFCs 3696 and 5321 - kwargs.setdefault('max_length', 254) + kwargs.setdefault("max_length", 254) super().__init__(*args, **kwargs) def deconstruct(self): @@ -1655,20 +1797,31 @@ class EmailField(CharField): def formfield(self, **kwargs): # As with CharField, this will cause email validation to be performed # twice. - return super().formfield(**{ - 'form_class': forms.EmailField, - **kwargs, - }) + return super().formfield( + **{ + "form_class": forms.EmailField, + **kwargs, + } + ) class FilePathField(Field): description = _("File path") - def __init__(self, verbose_name=None, name=None, path='', match=None, - recursive=False, allow_files=True, allow_folders=False, **kwargs): + def __init__( + self, + verbose_name=None, + name=None, + path="", + match=None, + recursive=False, + allow_files=True, + allow_folders=False, + **kwargs, + ): self.path, self.match, self.recursive = path, match, recursive self.allow_files, self.allow_folders = allow_files, allow_folders - kwargs.setdefault('max_length', 100) + kwargs.setdefault("max_length", 100) super().__init__(verbose_name, name, **kwargs) def check(self, **kwargs): @@ -1683,23 +1836,23 @@ class FilePathField(Field): checks.Error( "FilePathFields must have either 'allow_files' or 'allow_folders' set to True.", obj=self, - id='fields.E140', + id="fields.E140", ) ] return [] def deconstruct(self): name, path, args, kwargs = super().deconstruct() - if self.path != '': - kwargs['path'] = self.path + if self.path != "": + kwargs["path"] = self.path if self.match is not None: - kwargs['match'] = self.match + kwargs["match"] = self.match if self.recursive is not False: - kwargs['recursive'] = self.recursive + kwargs["recursive"] = self.recursive if self.allow_files is not True: - kwargs['allow_files'] = self.allow_files + kwargs["allow_files"] = self.allow_files if self.allow_folders is not False: - kwargs['allow_folders'] = self.allow_folders + kwargs["allow_folders"] = self.allow_folders if kwargs.get("max_length") == 100: del kwargs["max_length"] return name, path, args, kwargs @@ -1711,15 +1864,17 @@ class FilePathField(Field): return str(value) def formfield(self, **kwargs): - return super().formfield(**{ - 'path': self.path() if callable(self.path) else self.path, - 'match': self.match, - 'recursive': self.recursive, - 'form_class': forms.FilePathField, - 'allow_files': self.allow_files, - 'allow_folders': self.allow_folders, - **kwargs, - }) + return super().formfield( + **{ + "path": self.path() if callable(self.path) else self.path, + "match": self.match, + "recursive": self.recursive, + "form_class": forms.FilePathField, + "allow_files": self.allow_files, + "allow_folders": self.allow_folders, + **kwargs, + } + ) def get_internal_type(self): return "FilePathField" @@ -1728,7 +1883,7 @@ class FilePathField(Field): class FloatField(Field): empty_strings_allowed = False default_error_messages = { - 'invalid': _('“%(value)s” value must be a float.'), + "invalid": _("“%(value)s” value must be a float."), } description = _("Floating point number") @@ -1753,22 +1908,24 @@ class FloatField(Field): return float(value) except (TypeError, ValueError): raise exceptions.ValidationError( - self.error_messages['invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid"], + code="invalid", + params={"value": value}, ) def formfield(self, **kwargs): - return super().formfield(**{ - 'form_class': forms.FloatField, - **kwargs, - }) + return super().formfield( + **{ + "form_class": forms.FloatField, + **kwargs, + } + ) class IntegerField(Field): empty_strings_allowed = False default_error_messages = { - 'invalid': _('“%(value)s” value must be an integer.'), + "invalid": _("“%(value)s” value must be an integer."), } description = _("Integer") @@ -1782,10 +1939,11 @@ class IntegerField(Field): if self.max_length is not None: return [ checks.Warning( - "'max_length' is ignored when used with %s." % self.__class__.__name__, + "'max_length' is ignored when used with %s." + % self.__class__.__name__, hint="Remove 'max_length' from field", obj=self, - id='fields.W122', + id="fields.W122", ) ] return [] @@ -1799,22 +1957,28 @@ class IntegerField(Field): min_value, max_value = connection.ops.integer_field_range(internal_type) if min_value is not None and not any( ( - isinstance(validator, validators.MinValueValidator) and ( + isinstance(validator, validators.MinValueValidator) + and ( validator.limit_value() if callable(validator.limit_value) else validator.limit_value - ) >= min_value - ) for validator in validators_ + ) + >= min_value + ) + for validator in validators_ ): validators_.append(validators.MinValueValidator(min_value)) if max_value is not None and not any( ( - isinstance(validator, validators.MaxValueValidator) and ( + isinstance(validator, validators.MaxValueValidator) + and ( validator.limit_value() if callable(validator.limit_value) else validator.limit_value - ) <= max_value - ) for validator in validators_ + ) + <= max_value + ) + for validator in validators_ ): validators_.append(validators.MaxValueValidator(max_value)) return validators_ @@ -1840,16 +2004,18 @@ class IntegerField(Field): return int(value) except (TypeError, ValueError): raise exceptions.ValidationError( - self.error_messages['invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid"], + code="invalid", + params={"value": value}, ) def formfield(self, **kwargs): - return super().formfield(**{ - 'form_class': forms.IntegerField, - **kwargs, - }) + return super().formfield( + **{ + "form_class": forms.IntegerField, + **kwargs, + } + ) class BigIntegerField(IntegerField): @@ -1860,39 +2026,41 @@ class BigIntegerField(IntegerField): return "BigIntegerField" def formfield(self, **kwargs): - return super().formfield(**{ - 'min_value': -BigIntegerField.MAX_BIGINT - 1, - 'max_value': BigIntegerField.MAX_BIGINT, - **kwargs, - }) + return super().formfield( + **{ + "min_value": -BigIntegerField.MAX_BIGINT - 1, + "max_value": BigIntegerField.MAX_BIGINT, + **kwargs, + } + ) class SmallIntegerField(IntegerField): - description = _('Small integer') + description = _("Small integer") def get_internal_type(self): - return 'SmallIntegerField' + return "SmallIntegerField" class IPAddressField(Field): empty_strings_allowed = False description = _("IPv4 address") system_check_removed_details = { - 'msg': ( - 'IPAddressField has been removed except for support in ' - 'historical migrations.' + "msg": ( + "IPAddressField has been removed except for support in " + "historical migrations." ), - 'hint': 'Use GenericIPAddressField instead.', - 'id': 'fields.E900', + "hint": "Use GenericIPAddressField instead.", + "id": "fields.E900", } def __init__(self, *args, **kwargs): - kwargs['max_length'] = 15 + kwargs["max_length"] = 15 super().__init__(*args, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() - del kwargs['max_length'] + del kwargs["max_length"] return name, path, args, kwargs def get_prep_value(self, value): @@ -1910,14 +2078,23 @@ class GenericIPAddressField(Field): description = _("IP address") default_error_messages = {} - def __init__(self, verbose_name=None, name=None, protocol='both', - unpack_ipv4=False, *args, **kwargs): + def __init__( + self, + verbose_name=None, + name=None, + protocol="both", + unpack_ipv4=False, + *args, + **kwargs, + ): self.unpack_ipv4 = unpack_ipv4 self.protocol = protocol - self.default_validators, invalid_error_message = \ - validators.ip_address_validators(protocol, unpack_ipv4) - self.default_error_messages['invalid'] = invalid_error_message - kwargs['max_length'] = 39 + ( + self.default_validators, + invalid_error_message, + ) = validators.ip_address_validators(protocol, unpack_ipv4) + self.default_error_messages["invalid"] = invalid_error_message + kwargs["max_length"] = 39 super().__init__(verbose_name, name, *args, **kwargs) def check(self, **kwargs): @@ -1927,13 +2104,13 @@ class GenericIPAddressField(Field): ] def _check_blank_and_null_values(self, **kwargs): - if not getattr(self, 'null', False) and getattr(self, 'blank', False): + if not getattr(self, "null", False) and getattr(self, "blank", False): return [ checks.Error( - 'GenericIPAddressFields cannot have blank=True if null=False, ' - 'as blank values are stored as nulls.', + "GenericIPAddressFields cannot have blank=True if null=False, " + "as blank values are stored as nulls.", obj=self, - id='fields.E150', + id="fields.E150", ) ] return [] @@ -1941,11 +2118,11 @@ class GenericIPAddressField(Field): def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.unpack_ipv4 is not False: - kwargs['unpack_ipv4'] = self.unpack_ipv4 + kwargs["unpack_ipv4"] = self.unpack_ipv4 if self.protocol != "both": - kwargs['protocol'] = self.protocol + kwargs["protocol"] = self.protocol if kwargs.get("max_length") == 39: - del kwargs['max_length'] + del kwargs["max_length"] return name, path, args, kwargs def get_internal_type(self): @@ -1957,8 +2134,10 @@ class GenericIPAddressField(Field): if not isinstance(value, str): value = str(value) value = value.strip() - if ':' in value: - return clean_ipv6_address(value, self.unpack_ipv4, self.error_messages['invalid']) + if ":" in value: + return clean_ipv6_address( + value, self.unpack_ipv4, self.error_messages["invalid"] + ) return value def get_db_prep_value(self, value, connection, prepared=False): @@ -1970,7 +2149,7 @@ class GenericIPAddressField(Field): value = super().get_prep_value(value) if value is None: return None - if value and ':' in value: + if value and ":" in value: try: return clean_ipv6_address(value, self.unpack_ipv4) except exceptions.ValidationError: @@ -1978,44 +2157,46 @@ class GenericIPAddressField(Field): return str(value) def formfield(self, **kwargs): - return super().formfield(**{ - 'protocol': self.protocol, - 'form_class': forms.GenericIPAddressField, - **kwargs, - }) + return super().formfield( + **{ + "protocol": self.protocol, + "form_class": forms.GenericIPAddressField, + **kwargs, + } + ) class NullBooleanField(BooleanField): default_error_messages = { - 'invalid': _('“%(value)s” value must be either None, True or False.'), - 'invalid_nullable': _('“%(value)s” value must be either None, True or False.'), + "invalid": _("“%(value)s” value must be either None, True or False."), + "invalid_nullable": _("“%(value)s” value must be either None, True or False."), } description = _("Boolean (Either True, False or None)") system_check_removed_details = { - 'msg': ( - 'NullBooleanField is removed except for support in historical ' - 'migrations.' + "msg": ( + "NullBooleanField is removed except for support in historical " + "migrations." ), - 'hint': 'Use BooleanField(null=True) instead.', - 'id': 'fields.E903', + "hint": "Use BooleanField(null=True) instead.", + "id": "fields.E903", } def __init__(self, *args, **kwargs): - kwargs['null'] = True - kwargs['blank'] = True + kwargs["null"] = True + kwargs["blank"] = True super().__init__(*args, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() - del kwargs['null'] - del kwargs['blank'] + del kwargs["null"] + del kwargs["blank"] return name, path, args, kwargs class PositiveIntegerRelDbTypeMixin: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) - if not hasattr(cls, 'integer_field_class'): + if not hasattr(cls, "integer_field_class"): cls.integer_field_class = next( ( parent @@ -2041,16 +2222,18 @@ class PositiveIntegerRelDbTypeMixin: class PositiveBigIntegerField(PositiveIntegerRelDbTypeMixin, BigIntegerField): - description = _('Positive big integer') + description = _("Positive big integer") def get_internal_type(self): - return 'PositiveBigIntegerField' + return "PositiveBigIntegerField" def formfield(self, **kwargs): - return super().formfield(**{ - 'min_value': 0, - **kwargs, - }) + return super().formfield( + **{ + "min_value": 0, + **kwargs, + } + ) class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): @@ -2060,10 +2243,12 @@ class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): return "PositiveIntegerField" def formfield(self, **kwargs): - return super().formfield(**{ - 'min_value': 0, - **kwargs, - }) + return super().formfield( + **{ + "min_value": 0, + **kwargs, + } + ) class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, SmallIntegerField): @@ -2073,17 +2258,21 @@ class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, SmallIntegerField return "PositiveSmallIntegerField" def formfield(self, **kwargs): - return super().formfield(**{ - 'min_value': 0, - **kwargs, - }) + return super().formfield( + **{ + "min_value": 0, + **kwargs, + } + ) class SlugField(CharField): default_validators = [validators.validate_slug] description = _("Slug (up to %(max_length)s)") - def __init__(self, *args, max_length=50, db_index=True, allow_unicode=False, **kwargs): + def __init__( + self, *args, max_length=50, db_index=True, allow_unicode=False, **kwargs + ): self.allow_unicode = allow_unicode if self.allow_unicode: self.default_validators = [validators.validate_unicode_slug] @@ -2092,24 +2281,26 @@ class SlugField(CharField): def deconstruct(self): name, path, args, kwargs = super().deconstruct() if kwargs.get("max_length") == 50: - del kwargs['max_length'] + del kwargs["max_length"] if self.db_index is False: - kwargs['db_index'] = False + kwargs["db_index"] = False else: - del kwargs['db_index'] + del kwargs["db_index"] if self.allow_unicode is not False: - kwargs['allow_unicode'] = self.allow_unicode + kwargs["allow_unicode"] = self.allow_unicode return name, path, args, kwargs def get_internal_type(self): return "SlugField" def formfield(self, **kwargs): - return super().formfield(**{ - 'form_class': forms.SlugField, - 'allow_unicode': self.allow_unicode, - **kwargs, - }) + return super().formfield( + **{ + "form_class": forms.SlugField, + "allow_unicode": self.allow_unicode, + **kwargs, + } + ) class TextField(Field): @@ -2120,7 +2311,7 @@ class TextField(Field): self.db_collation = db_collation def check(self, **kwargs): - databases = kwargs.get('databases') or [] + databases = kwargs.get("databases") or [] return [ *super().check(**kwargs), *self._check_db_collation(databases), @@ -2133,16 +2324,17 @@ class TextField(Field): continue connection = connections[db] if not ( - self.db_collation is None or - 'supports_collation_on_textfield' in self.model._meta.required_db_features or - connection.features.supports_collation_on_textfield + self.db_collation is None + or "supports_collation_on_textfield" + in self.model._meta.required_db_features + or connection.features.supports_collation_on_textfield ): errors.append( checks.Error( - '%s does not support a database collation on ' - 'TextFields.' % connection.display_name, + "%s does not support a database collation on " + "TextFields." % connection.display_name, obj=self, - id='fields.E190', + id="fields.E190", ), ) return errors @@ -2163,35 +2355,42 @@ class TextField(Field): # Passing max_length to forms.CharField means that the value's length # will be validated twice. This is considered acceptable since we want # the value in the form field (to pass into widget for example). - return super().formfield(**{ - 'max_length': self.max_length, - **({} if self.choices is not None else {'widget': forms.Textarea}), - **kwargs, - }) + return super().formfield( + **{ + "max_length": self.max_length, + **({} if self.choices is not None else {"widget": forms.Textarea}), + **kwargs, + } + ) def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.db_collation: - kwargs['db_collation'] = self.db_collation + kwargs["db_collation"] = self.db_collation return name, path, args, kwargs class TimeField(DateTimeCheckMixin, Field): empty_strings_allowed = False default_error_messages = { - 'invalid': _('“%(value)s” value has an invalid format. It must be in ' - 'HH:MM[:ss[.uuuuuu]] format.'), - 'invalid_time': _('“%(value)s” value has the correct format ' - '(HH:MM[:ss[.uuuuuu]]) but it is an invalid time.'), + "invalid": _( + "“%(value)s” value has an invalid format. It must be in " + "HH:MM[:ss[.uuuuuu]] format." + ), + "invalid_time": _( + "“%(value)s” value has the correct format " + "(HH:MM[:ss[.uuuuuu]]) but it is an invalid time." + ), } description = _("Time") - def __init__(self, verbose_name=None, name=None, auto_now=False, - auto_now_add=False, **kwargs): + def __init__( + self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs + ): self.auto_now, self.auto_now_add = auto_now, auto_now_add if auto_now or auto_now_add: - kwargs['editable'] = False - kwargs['blank'] = True + kwargs["editable"] = False + kwargs["blank"] = True super().__init__(verbose_name, name, **kwargs) def _check_fix_default_value(self): @@ -2223,8 +2422,8 @@ class TimeField(DateTimeCheckMixin, Field): if self.auto_now_add is not False: kwargs["auto_now_add"] = self.auto_now_add if self.auto_now or self.auto_now_add: - del kwargs['blank'] - del kwargs['editable'] + del kwargs["blank"] + del kwargs["editable"] return name, path, args, kwargs def get_internal_type(self): @@ -2247,15 +2446,15 @@ class TimeField(DateTimeCheckMixin, Field): return parsed except ValueError: raise exceptions.ValidationError( - self.error_messages['invalid_time'], - code='invalid_time', - params={'value': value}, + self.error_messages["invalid_time"], + code="invalid_time", + params={"value": value}, ) raise exceptions.ValidationError( - self.error_messages['invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid"], + code="invalid", + params={"value": value}, ) def pre_save(self, model_instance, add): @@ -2278,13 +2477,15 @@ class TimeField(DateTimeCheckMixin, Field): def value_to_string(self, obj): val = self.value_from_object(obj) - return '' if val is None else val.isoformat() + return "" if val is None else val.isoformat() def formfield(self, **kwargs): - return super().formfield(**{ - 'form_class': forms.TimeField, - **kwargs, - }) + return super().formfield( + **{ + "form_class": forms.TimeField, + **kwargs, + } + ) class URLField(CharField): @@ -2292,30 +2493,32 @@ class URLField(CharField): description = _("URL") def __init__(self, verbose_name=None, name=None, **kwargs): - kwargs.setdefault('max_length', 200) + kwargs.setdefault("max_length", 200) super().__init__(verbose_name, name, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() if kwargs.get("max_length") == 200: - del kwargs['max_length'] + del kwargs["max_length"] return name, path, args, kwargs def formfield(self, **kwargs): # As with CharField, this will cause URL validation to be performed # twice. - return super().formfield(**{ - 'form_class': forms.URLField, - **kwargs, - }) + return super().formfield( + **{ + "form_class": forms.URLField, + **kwargs, + } + ) class BinaryField(Field): description = _("Raw binary data") - empty_values = [None, b''] + empty_values = [None, b""] def __init__(self, *args, **kwargs): - kwargs.setdefault('editable', False) + kwargs.setdefault("editable", False) super().__init__(*args, **kwargs) if self.max_length is not None: self.validators.append(validators.MaxLengthValidator(self.max_length)) @@ -2330,7 +2533,7 @@ class BinaryField(Field): "BinaryField's default cannot be a string. Use bytes " "content instead.", obj=self, - id='fields.E170', + id="fields.E170", ) ] return [] @@ -2338,9 +2541,9 @@ class BinaryField(Field): def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.editable: - kwargs['editable'] = True + kwargs["editable"] = True else: - del kwargs['editable'] + del kwargs["editable"] return name, path, args, kwargs def get_internal_type(self): @@ -2353,8 +2556,8 @@ class BinaryField(Field): if self.has_default() and not callable(self.default): return self.default default = super().get_default() - if default == '': - return b'' + if default == "": + return b"" return default def get_db_prep_value(self, value, connection, prepared=False): @@ -2365,29 +2568,29 @@ class BinaryField(Field): def value_to_string(self, obj): """Binary data is serialized as base64""" - return b64encode(self.value_from_object(obj)).decode('ascii') + return b64encode(self.value_from_object(obj)).decode("ascii") def to_python(self, value): # If it's a string, it should be base64-encoded data if isinstance(value, str): - return memoryview(b64decode(value.encode('ascii'))) + return memoryview(b64decode(value.encode("ascii"))) return value class UUIDField(Field): default_error_messages = { - 'invalid': _('“%(value)s” is not a valid UUID.'), + "invalid": _("“%(value)s” is not a valid UUID."), } - description = _('Universally unique identifier') + description = _("Universally unique identifier") empty_strings_allowed = False def __init__(self, verbose_name=None, **kwargs): - kwargs['max_length'] = 32 + kwargs["max_length"] = 32 super().__init__(verbose_name, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() - del kwargs['max_length'] + del kwargs["max_length"] return name, path, args, kwargs def get_internal_type(self): @@ -2409,29 +2612,31 @@ class UUIDField(Field): def to_python(self, value): if value is not None and not isinstance(value, uuid.UUID): - input_form = 'int' if isinstance(value, int) else 'hex' + input_form = "int" if isinstance(value, int) else "hex" try: return uuid.UUID(**{input_form: value}) except (AttributeError, ValueError): raise exceptions.ValidationError( - self.error_messages['invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid"], + code="invalid", + params={"value": value}, ) return value def formfield(self, **kwargs): - return super().formfield(**{ - 'form_class': forms.UUIDField, - **kwargs, - }) + return super().formfield( + **{ + "form_class": forms.UUIDField, + **kwargs, + } + ) class AutoFieldMixin: db_returning = True def __init__(self, *args, **kwargs): - kwargs['blank'] = True + kwargs["blank"] = True super().__init__(*args, **kwargs) def check(self, **kwargs): @@ -2444,9 +2649,9 @@ class AutoFieldMixin: if not self.primary_key: return [ checks.Error( - 'AutoFields must set primary_key=True.', + "AutoFields must set primary_key=True.", obj=self, - id='fields.E100', + id="fields.E100", ), ] else: @@ -2454,8 +2659,8 @@ class AutoFieldMixin: def deconstruct(self): name, path, args, kwargs = super().deconstruct() - del kwargs['blank'] - kwargs['primary_key'] = True + del kwargs["blank"] + kwargs["primary_key"] = True return name, path, args, kwargs def validate(self, value, model_instance): @@ -2502,34 +2707,35 @@ class AutoFieldMeta(type): return (BigAutoField, SmallAutoField) def __instancecheck__(self, instance): - return isinstance(instance, self._subclasses) or super().__instancecheck__(instance) + return isinstance(instance, self._subclasses) or super().__instancecheck__( + instance + ) def __subclasscheck__(self, subclass): - return issubclass(subclass, self._subclasses) or super().__subclasscheck__(subclass) + return issubclass(subclass, self._subclasses) or super().__subclasscheck__( + subclass + ) class AutoField(AutoFieldMixin, IntegerField, metaclass=AutoFieldMeta): - def get_internal_type(self): - return 'AutoField' + return "AutoField" def rel_db_type(self, connection): return IntegerField().db_type(connection=connection) class BigAutoField(AutoFieldMixin, BigIntegerField): - def get_internal_type(self): - return 'BigAutoField' + return "BigAutoField" def rel_db_type(self, connection): return BigIntegerField().db_type(connection=connection) class SmallAutoField(AutoFieldMixin, SmallIntegerField): - def get_internal_type(self): - return 'SmallAutoField' + return "SmallAutoField" def rel_db_type(self, connection): return SmallIntegerField().db_type(connection=connection) |
