diff options
Diffstat (limited to 'django/forms/fields.py')
| -rw-r--r-- | django/forms/fields.py | 437 |
1 files changed, 267 insertions, 170 deletions
diff --git a/django/forms/fields.py b/django/forms/fields.py index 65d6a9ec82..a7031936dd 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -19,45 +19,94 @@ from django.core.exceptions import ValidationError from django.forms.boundfield import BoundField from django.forms.utils import from_current_timezone, to_current_timezone from django.forms.widgets import ( - FILE_INPUT_CONTRADICTION, CheckboxInput, ClearableFileInput, DateInput, - DateTimeInput, EmailInput, FileInput, HiddenInput, MultipleHiddenInput, - NullBooleanSelect, NumberInput, Select, SelectMultiple, - SplitDateTimeWidget, SplitHiddenDateTimeWidget, Textarea, TextInput, - TimeInput, URLInput, + FILE_INPUT_CONTRADICTION, + CheckboxInput, + ClearableFileInput, + DateInput, + DateTimeInput, + EmailInput, + FileInput, + HiddenInput, + MultipleHiddenInput, + NullBooleanSelect, + NumberInput, + Select, + SelectMultiple, + SplitDateTimeWidget, + SplitHiddenDateTimeWidget, + Textarea, + TextInput, + TimeInput, + URLInput, ) from django.utils import formats from django.utils.dateparse import parse_datetime, parse_duration from django.utils.duration import duration_string from django.utils.ipv6 import clean_ipv6_address from django.utils.regex_helper import _lazy_re_compile -from django.utils.translation import gettext_lazy as _, ngettext_lazy +from django.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy __all__ = ( - 'Field', 'CharField', 'IntegerField', - 'DateField', 'TimeField', 'DateTimeField', 'DurationField', - 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', - 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', - 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', - 'SplitDateTimeField', 'GenericIPAddressField', 'FilePathField', - 'JSONField', 'SlugField', 'TypedChoiceField', 'TypedMultipleChoiceField', - 'UUIDField', + "Field", + "CharField", + "IntegerField", + "DateField", + "TimeField", + "DateTimeField", + "DurationField", + "RegexField", + "EmailField", + "FileField", + "ImageField", + "URLField", + "BooleanField", + "NullBooleanField", + "ChoiceField", + "MultipleChoiceField", + "ComboField", + "MultiValueField", + "FloatField", + "DecimalField", + "SplitDateTimeField", + "GenericIPAddressField", + "FilePathField", + "JSONField", + "SlugField", + "TypedChoiceField", + "TypedMultipleChoiceField", + "UUIDField", ) class Field: widget = TextInput # Default widget to use when rendering this type of Field. - hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden". + hidden_widget = ( + HiddenInput # Default widget to use when rendering this as "hidden". + ) default_validators = [] # Default set of validators # Add an 'invalid' entry to default_error_message if you want a specific # field error message not raised by the field validators. default_error_messages = { - 'required': _('This field is required.'), + "required": _("This field is required."), } empty_values = list(validators.EMPTY_VALUES) - def __init__(self, *, required=True, widget=None, label=None, initial=None, - help_text='', error_messages=None, show_hidden_initial=False, - validators=(), localize=False, disabled=False, label_suffix=None): + def __init__( + self, + *, + required=True, + widget=None, + label=None, + initial=None, + help_text="", + error_messages=None, + show_hidden_initial=False, + validators=(), + localize=False, + disabled=False, + label_suffix=None, + ): # required -- Boolean that specifies whether the field is required. # True by default. # widget -- A Widget class, or instance of a Widget class, that should @@ -109,7 +158,7 @@ class Field: 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 = messages @@ -125,7 +174,7 @@ class Field: def validate(self, value): if value in self.empty_values and self.required: - raise ValidationError(self.error_messages['required'], code='required') + raise ValidationError(self.error_messages["required"], code="required") def run_validators(self, value): if value in self.empty_values: @@ -135,7 +184,7 @@ class Field: try: v(value) except 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) if errors: @@ -180,15 +229,15 @@ class Field: return False try: data = self.to_python(data) - if hasattr(self, '_coerce'): + if hasattr(self, "_coerce"): return self._coerce(data) != self._coerce(initial) except ValidationError: return True # For purposes of seeing whether something has changed, None is # the same as an empty string, if the data or initial value we get # is None, replace it with ''. - initial_value = initial if initial is not None else '' - data_value = data if data is not None else '' + initial_value = initial if initial is not None else "" + data_value = data if data is not None else "" return initial_value != data_value def get_bound_field(self, form, field_name): @@ -208,7 +257,9 @@ class Field: class CharField(Field): - def __init__(self, *, max_length=None, min_length=None, strip=True, empty_value='', **kwargs): + def __init__( + self, *, max_length=None, min_length=None, strip=True, empty_value="", **kwargs + ): self.max_length = max_length self.min_length = min_length self.strip = strip @@ -234,25 +285,25 @@ class CharField(Field): attrs = super().widget_attrs(widget) if self.max_length is not None and not widget.is_hidden: # The HTML attribute is maxlength, not max_length. - attrs['maxlength'] = str(self.max_length) + attrs["maxlength"] = str(self.max_length) if self.min_length is not None and not widget.is_hidden: # The HTML attribute is minlength, not min_length. - attrs['minlength'] = str(self.min_length) + attrs["minlength"] = str(self.min_length) return attrs class IntegerField(Field): widget = NumberInput default_error_messages = { - 'invalid': _('Enter a whole number.'), + "invalid": _("Enter a whole number."), } - re_decimal = _lazy_re_compile(r'\.0*\s*$') + re_decimal = _lazy_re_compile(r"\.0*\s*$") def __init__(self, *, max_value=None, min_value=None, **kwargs): self.max_value, self.min_value = max_value, min_value - if kwargs.get('localize') and self.widget == NumberInput: + if kwargs.get("localize") and self.widget == NumberInput: # Localized number input is not well supported on most browsers - kwargs.setdefault('widget', super().widget) + kwargs.setdefault("widget", super().widget) super().__init__(**kwargs) if max_value is not None: @@ -272,24 +323,24 @@ class IntegerField(Field): value = formats.sanitize_separators(value) # Strip trailing decimal and zeros. try: - value = int(self.re_decimal.sub('', str(value))) + value = int(self.re_decimal.sub("", str(value))) except (ValueError, TypeError): - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") return value def widget_attrs(self, widget): attrs = super().widget_attrs(widget) if isinstance(widget, NumberInput): if self.min_value is not None: - attrs['min'] = self.min_value + attrs["min"] = self.min_value if self.max_value is not None: - attrs['max'] = self.max_value + attrs["max"] = self.max_value return attrs class FloatField(IntegerField): default_error_messages = { - 'invalid': _('Enter a number.'), + "invalid": _("Enter a number."), } def to_python(self, value): @@ -305,7 +356,7 @@ class FloatField(IntegerField): try: value = float(value) except (ValueError, TypeError): - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") return value def validate(self, value): @@ -313,21 +364,29 @@ class FloatField(IntegerField): if value in self.empty_values: return if not math.isfinite(value): - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") def widget_attrs(self, widget): attrs = super().widget_attrs(widget) - if isinstance(widget, NumberInput) and 'step' not in widget.attrs: - attrs.setdefault('step', 'any') + if isinstance(widget, NumberInput) and "step" not in widget.attrs: + attrs.setdefault("step", "any") return attrs class DecimalField(IntegerField): default_error_messages = { - 'invalid': _('Enter a number.'), + "invalid": _("Enter a number."), } - def __init__(self, *, max_value=None, min_value=None, max_digits=None, decimal_places=None, **kwargs): + def __init__( + self, + *, + max_value=None, + min_value=None, + max_digits=None, + decimal_places=None, + **kwargs, + ): self.max_digits, self.decimal_places = max_digits, decimal_places super().__init__(max_value=max_value, min_value=min_value, **kwargs) self.validators.append(validators.DecimalValidator(max_digits, decimal_places)) @@ -346,7 +405,7 @@ class DecimalField(IntegerField): try: value = Decimal(str(value)) except DecimalException: - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") return value def validate(self, value): @@ -355,26 +414,25 @@ class DecimalField(IntegerField): return if not value.is_finite(): raise ValidationError( - self.error_messages['invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid"], + code="invalid", + params={"value": value}, ) def widget_attrs(self, widget): attrs = super().widget_attrs(widget) - if isinstance(widget, NumberInput) and 'step' not in widget.attrs: + if isinstance(widget, NumberInput) and "step" not in widget.attrs: if self.decimal_places is not None: # Use exponential notation for small values since they might # be parsed as 0 otherwise. ref #20765 step = str(Decimal(1).scaleb(-self.decimal_places)).lower() else: - step = 'any' - attrs.setdefault('step', step) + step = "any" + attrs.setdefault("step", step) return attrs class BaseTemporalField(Field): - def __init__(self, *, input_formats=None, **kwargs): super().__init__(**kwargs) if input_formats is not None: @@ -388,17 +446,17 @@ class BaseTemporalField(Field): return self.strptime(value, format) except (ValueError, TypeError): continue - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") def strptime(self, value, format): - raise NotImplementedError('Subclasses must define this method.') + raise NotImplementedError("Subclasses must define this method.") class DateField(BaseTemporalField): widget = DateInput - input_formats = formats.get_format_lazy('DATE_INPUT_FORMATS') + input_formats = formats.get_format_lazy("DATE_INPUT_FORMATS") default_error_messages = { - 'invalid': _('Enter a valid date.'), + "invalid": _("Enter a valid date."), } def to_python(self, value): @@ -420,10 +478,8 @@ class DateField(BaseTemporalField): class TimeField(BaseTemporalField): widget = TimeInput - input_formats = formats.get_format_lazy('TIME_INPUT_FORMATS') - default_error_messages = { - 'invalid': _('Enter a valid time.') - } + input_formats = formats.get_format_lazy("TIME_INPUT_FORMATS") + default_error_messages = {"invalid": _("Enter a valid time.")} def to_python(self, value): """ @@ -442,15 +498,15 @@ class TimeField(BaseTemporalField): class DateTimeFormatsIterator: def __iter__(self): - yield from formats.get_format('DATETIME_INPUT_FORMATS') - yield from formats.get_format('DATE_INPUT_FORMATS') + yield from formats.get_format("DATETIME_INPUT_FORMATS") + yield from formats.get_format("DATE_INPUT_FORMATS") class DateTimeField(BaseTemporalField): widget = DateTimeInput input_formats = DateTimeFormatsIterator() default_error_messages = { - 'invalid': _('Enter a valid date/time.'), + "invalid": _("Enter a valid date/time."), } def prepare_value(self, value): @@ -473,7 +529,7 @@ class DateTimeField(BaseTemporalField): try: result = parse_datetime(value.strip()) except ValueError: - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") if not result: result = super().to_python(value) return from_current_timezone(result) @@ -484,8 +540,8 @@ class DateTimeField(BaseTemporalField): class DurationField(Field): default_error_messages = { - 'invalid': _('Enter a valid duration.'), - 'overflow': _('The number of days must be between {min_days} and {max_days}.') + "invalid": _("Enter a valid duration."), + "overflow": _("The number of days must be between {min_days} and {max_days}."), } def prepare_value(self, value): @@ -501,12 +557,15 @@ class DurationField(Field): try: value = parse_duration(str(value)) except OverflowError: - raise ValidationError(self.error_messages['overflow'].format( - min_days=datetime.timedelta.min.days, - max_days=datetime.timedelta.max.days, - ), code='overflow') + raise ValidationError( + self.error_messages["overflow"].format( + min_days=datetime.timedelta.min.days, + max_days=datetime.timedelta.max.days, + ), + code="overflow", + ) if value is None: - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") return value @@ -515,7 +574,7 @@ class RegexField(CharField): """ regex can be either a string or a compiled regular expression object. """ - kwargs.setdefault('strip', False) + kwargs.setdefault("strip", False) super().__init__(**kwargs) self._set_regex(regex) @@ -526,7 +585,10 @@ class RegexField(CharField): if isinstance(regex, str): regex = re.compile(regex) self._regex = regex - if hasattr(self, '_regex_validator') and self._regex_validator in self.validators: + if ( + hasattr(self, "_regex_validator") + and self._regex_validator in self.validators + ): self.validators.remove(self._regex_validator) self._regex_validator = validators.RegexValidator(regex=regex) self.validators.append(self._regex_validator) @@ -545,14 +607,17 @@ class EmailField(CharField): class FileField(Field): widget = ClearableFileInput default_error_messages = { - 'invalid': _("No file was submitted. Check the encoding type on the form."), - 'missing': _("No file was submitted."), - 'empty': _("The submitted file is empty."), - 'max_length': ngettext_lazy( - 'Ensure this filename has at most %(max)d character (it has %(length)d).', - 'Ensure this filename has at most %(max)d characters (it has %(length)d).', - 'max'), - 'contradiction': _('Please either submit a file or check the clear checkbox, not both.') + "invalid": _("No file was submitted. Check the encoding type on the form."), + "missing": _("No file was submitted."), + "empty": _("The submitted file is empty."), + "max_length": ngettext_lazy( + "Ensure this filename has at most %(max)d character (it has %(length)d).", + "Ensure this filename has at most %(max)d characters (it has %(length)d).", + "max", + ), + "contradiction": _( + "Please either submit a file or check the clear checkbox, not both." + ), } def __init__(self, *, max_length=None, allow_empty_file=False, **kwargs): @@ -569,22 +634,26 @@ class FileField(Field): file_name = data.name file_size = data.size except AttributeError: - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") if self.max_length is not None and len(file_name) > self.max_length: - params = {'max': self.max_length, 'length': len(file_name)} - raise ValidationError(self.error_messages['max_length'], code='max_length', params=params) + params = {"max": self.max_length, "length": len(file_name)} + raise ValidationError( + self.error_messages["max_length"], code="max_length", params=params + ) if not file_name: - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") if not self.allow_empty_file and not file_size: - raise ValidationError(self.error_messages['empty'], code='empty') + raise ValidationError(self.error_messages["empty"], code="empty") return data def clean(self, data, initial=None): # If the widget got contradictory inputs, we raise a validation error if data is FILE_INPUT_CONTRADICTION: - raise ValidationError(self.error_messages['contradiction'], code='contradiction') + raise ValidationError( + self.error_messages["contradiction"], code="contradiction" + ) # False means the field value should be cleared; further validation is # not needed. if data is False: @@ -612,7 +681,7 @@ class FileField(Field): class ImageField(FileField): default_validators = [validators.validate_image_file_extension] default_error_messages = { - 'invalid_image': _( + "invalid_image": _( "Upload a valid image. The file you uploaded was either not an " "image or a corrupted image." ), @@ -631,13 +700,13 @@ class ImageField(FileField): # We need to get a file object for Pillow. We might have a path or we might # have to read the data into memory. - if hasattr(data, 'temporary_file_path'): + if hasattr(data, "temporary_file_path"): file = data.temporary_file_path() else: - if hasattr(data, 'read'): + if hasattr(data, "read"): file = BytesIO(data.read()) else: - file = BytesIO(data['content']) + file = BytesIO(data["content"]) try: # load() could spot a truncated JPEG, but it loads the entire @@ -654,24 +723,24 @@ class ImageField(FileField): except Exception as exc: # Pillow doesn't recognize it as an image. raise ValidationError( - self.error_messages['invalid_image'], - code='invalid_image', + self.error_messages["invalid_image"], + code="invalid_image", ) from exc - if hasattr(f, 'seek') and callable(f.seek): + if hasattr(f, "seek") and callable(f.seek): f.seek(0) return f def widget_attrs(self, widget): attrs = super().widget_attrs(widget) - if isinstance(widget, FileInput) and 'accept' not in widget.attrs: - attrs.setdefault('accept', 'image/*') + if isinstance(widget, FileInput) and "accept" not in widget.attrs: + attrs.setdefault("accept", "image/*") return attrs class URLField(CharField): widget = URLInput default_error_messages = { - 'invalid': _('Enter a valid URL.'), + "invalid": _("Enter a valid URL."), } default_validators = [validators.URLValidator()] @@ -679,7 +748,6 @@ class URLField(CharField): super().__init__(strip=True, **kwargs) def to_python(self, value): - def split_url(url): """ Return a list of url parts via urlparse.urlsplit(), or raise @@ -690,19 +758,19 @@ class URLField(CharField): except ValueError: # urlparse.urlsplit can raise a ValueError with some # misformatted URLs. - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") value = super().to_python(value) if value: url_fields = split_url(value) if not url_fields[0]: # If no URL scheme given, assume http:// - url_fields[0] = 'http' + url_fields[0] = "http" if not url_fields[1]: # Assume that if no domain is provided, that the path segment # contains the domain. url_fields[1] = url_fields[2] - url_fields[2] = '' + url_fields[2] = "" # Rebuild the url_fields list, since the domain segment may now # contain the path too. url_fields = split_url(urlunsplit(url_fields)) @@ -719,7 +787,7 @@ class BooleanField(Field): # will submit for False. Also check for '0', since this is what # RadioSelect will provide. Because bool("True") == bool('1') == True, # we don't need to handle that explicitly. - if isinstance(value, str) and value.lower() in ('false', '0'): + if isinstance(value, str) and value.lower() in ("false", "0"): value = False else: value = bool(value) @@ -727,7 +795,7 @@ class BooleanField(Field): def validate(self, value): if not value and self.required: - raise ValidationError(self.error_messages['required'], code='required') + raise ValidationError(self.error_messages["required"], code="required") def has_changed(self, initial, data): if self.disabled: @@ -742,6 +810,7 @@ class NullBooleanField(BooleanField): A field whose valid values are None, True, and False. Clean invalid values to None. """ + widget = NullBooleanSelect def to_python(self, value): @@ -753,9 +822,9 @@ class NullBooleanField(BooleanField): the Booleanfield, this field must check for True because it doesn't use the bool() function. """ - if value in (True, 'True', 'true', '1'): + if value in (True, "True", "true", "1"): return True - elif value in (False, 'False', 'false', '0'): + elif value in (False, "False", "false", "0"): return False else: return None @@ -775,7 +844,9 @@ class CallableChoiceIterator: class ChoiceField(Field): widget = Select default_error_messages = { - 'invalid_choice': _('Select a valid choice. %(value)s is not one of the available choices.'), + "invalid_choice": _( + "Select a valid choice. %(value)s is not one of the available choices." + ), } def __init__(self, *, choices=(), **kwargs): @@ -806,7 +877,7 @@ class ChoiceField(Field): def to_python(self, value): """Return a string.""" if value in self.empty_values: - return '' + return "" return str(value) def validate(self, value): @@ -814,9 +885,9 @@ class ChoiceField(Field): super().validate(value) if value and not self.valid_value(value): raise ValidationError( - self.error_messages['invalid_choice'], - code='invalid_choice', - params={'value': value}, + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": value}, ) def valid_value(self, value): @@ -835,7 +906,7 @@ class ChoiceField(Field): class TypedChoiceField(ChoiceField): - def __init__(self, *, coerce=lambda val: val, empty_value='', **kwargs): + def __init__(self, *, coerce=lambda val: val, empty_value="", **kwargs): self.coerce = coerce self.empty_value = empty_value super().__init__(**kwargs) @@ -850,9 +921,9 @@ class TypedChoiceField(ChoiceField): value = self.coerce(value) except (ValueError, TypeError, ValidationError): raise ValidationError( - self.error_messages['invalid_choice'], - code='invalid_choice', - params={'value': value}, + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": value}, ) return value @@ -865,28 +936,32 @@ class MultipleChoiceField(ChoiceField): hidden_widget = MultipleHiddenInput widget = SelectMultiple default_error_messages = { - 'invalid_choice': _('Select a valid choice. %(value)s is not one of the available choices.'), - 'invalid_list': _('Enter a list of values.'), + "invalid_choice": _( + "Select a valid choice. %(value)s is not one of the available choices." + ), + "invalid_list": _("Enter a list of values."), } def to_python(self, value): if not value: return [] elif not isinstance(value, (list, tuple)): - raise ValidationError(self.error_messages['invalid_list'], code='invalid_list') + raise ValidationError( + self.error_messages["invalid_list"], code="invalid_list" + ) return [str(val) for val in value] def validate(self, value): """Validate that the input is a list or tuple.""" if self.required and not value: - raise ValidationError(self.error_messages['required'], code='required') + raise ValidationError(self.error_messages["required"], code="required") # Validate that each value in the value list is in self.choices. for val in value: if not self.valid_value(val): raise ValidationError( - self.error_messages['invalid_choice'], - code='invalid_choice', - params={'value': val}, + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": val}, ) def has_changed(self, initial, data): @@ -906,7 +981,7 @@ class MultipleChoiceField(ChoiceField): class TypedMultipleChoiceField(MultipleChoiceField): def __init__(self, *, coerce=lambda val: val, **kwargs): self.coerce = coerce - self.empty_value = kwargs.pop('empty_value', []) + self.empty_value = kwargs.pop("empty_value", []) super().__init__(**kwargs) def _coerce(self, value): @@ -922,9 +997,9 @@ class TypedMultipleChoiceField(MultipleChoiceField): new_value.append(self.coerce(choice)) except (ValueError, TypeError, ValidationError): raise ValidationError( - self.error_messages['invalid_choice'], - code='invalid_choice', - params={'value': choice}, + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": choice}, ) return new_value @@ -936,13 +1011,14 @@ class TypedMultipleChoiceField(MultipleChoiceField): if value != self.empty_value: super().validate(value) elif self.required: - raise ValidationError(self.error_messages['required'], code='required') + raise ValidationError(self.error_messages["required"], code="required") class ComboField(Field): """ A Field whose clean() method calls multiple Field clean() methods. """ + def __init__(self, fields, **kwargs): super().__init__(**kwargs) # Set 'required' to False on the individual fields, because the @@ -980,17 +1056,17 @@ class MultiValueField(Field): You'll probably want to use this with MultiWidget. """ + default_error_messages = { - 'invalid': _('Enter a list of values.'), - 'incomplete': _('Enter a complete value.'), + "invalid": _("Enter a list of values."), + "incomplete": _("Enter a complete value."), } def __init__(self, fields, *, require_all_fields=True, **kwargs): self.require_all_fields = require_all_fields super().__init__(**kwargs) for f in fields: - f.error_messages.setdefault('incomplete', - self.error_messages['incomplete']) + f.error_messages.setdefault("incomplete", self.error_messages["incomplete"]) if self.disabled: f.disabled = True if self.require_all_fields: @@ -1024,11 +1100,13 @@ class MultiValueField(Field): if not value or isinstance(value, (list, tuple)): if not value or not [v for v in value if v not in self.empty_values]: if self.required: - raise ValidationError(self.error_messages['required'], code='required') + raise ValidationError( + self.error_messages["required"], code="required" + ) else: return self.compress([]) else: - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") for i, field in enumerate(self.fields): try: field_value = value[i] @@ -1039,13 +1117,15 @@ class MultiValueField(Field): # Raise a 'required' error if the MultiValueField is # required and any field is empty. if self.required: - raise ValidationError(self.error_messages['required'], code='required') + raise ValidationError( + self.error_messages["required"], code="required" + ) elif field.required: # Otherwise, add an 'incomplete' error to the list of # collected errors and skip field cleaning, if a required # field is empty. - if field.error_messages['incomplete'] not in errors: - errors.append(field.error_messages['incomplete']) + if field.error_messages["incomplete"] not in errors: + errors.append(field.error_messages["incomplete"]) continue try: clean_data.append(field.clean(field_value)) @@ -1071,13 +1151,13 @@ class MultiValueField(Field): fields=(DateField(), TimeField()), this might return a datetime object created by combining the date and time in data_list. """ - raise NotImplementedError('Subclasses must implement this method.') + raise NotImplementedError("Subclasses must implement this method.") def has_changed(self, initial, data): if self.disabled: return False if initial is None: - initial = ['' for x in range(0, len(data))] + initial = ["" for x in range(0, len(data))] else: if not isinstance(initial, list): initial = self.widget.decompress(initial) @@ -1092,8 +1172,16 @@ class MultiValueField(Field): class FilePathField(ChoiceField): - def __init__(self, path, *, match=None, recursive=False, allow_files=True, - allow_folders=False, **kwargs): + def __init__( + self, + 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 super().__init__(choices=(), **kwargs) @@ -1115,7 +1203,7 @@ class FilePathField(ChoiceField): self.choices.append((f, f.replace(path, "", 1))) if self.allow_folders: for f in sorted(dirs): - if f == '__pycache__': + if f == "__pycache__": continue if self.match is None or self.match_re.search(f): f = os.path.join(root, f) @@ -1124,12 +1212,12 @@ class FilePathField(ChoiceField): choices = [] with os.scandir(self.path) as entries: for f in entries: - if f.name == '__pycache__': + if f.name == "__pycache__": continue - if (( - (self.allow_files and f.is_file()) or - (self.allow_folders and f.is_dir()) - ) and (self.match is None or self.match_re.search(f.name))): + if ( + (self.allow_files and f.is_file()) + or (self.allow_folders and f.is_dir()) + ) and (self.match is None or self.match_re.search(f.name)): choices.append((f.path, f.name)) choices.sort(key=operator.itemgetter(1)) self.choices.extend(choices) @@ -1141,22 +1229,26 @@ class SplitDateTimeField(MultiValueField): widget = SplitDateTimeWidget hidden_widget = SplitHiddenDateTimeWidget default_error_messages = { - 'invalid_date': _('Enter a valid date.'), - 'invalid_time': _('Enter a valid time.'), + "invalid_date": _("Enter a valid date."), + "invalid_time": _("Enter a valid time."), } def __init__(self, *, input_date_formats=None, input_time_formats=None, **kwargs): errors = self.default_error_messages.copy() - if 'error_messages' in kwargs: - errors.update(kwargs['error_messages']) - localize = kwargs.get('localize', False) + if "error_messages" in kwargs: + errors.update(kwargs["error_messages"]) + localize = kwargs.get("localize", False) fields = ( - DateField(input_formats=input_date_formats, - error_messages={'invalid': errors['invalid_date']}, - localize=localize), - TimeField(input_formats=input_time_formats, - error_messages={'invalid': errors['invalid_time']}, - localize=localize), + DateField( + input_formats=input_date_formats, + error_messages={"invalid": errors["invalid_date"]}, + localize=localize, + ), + TimeField( + input_formats=input_time_formats, + error_messages={"invalid": errors["invalid_time"]}, + localize=localize, + ), ) super().__init__(fields, **kwargs) @@ -1165,25 +1257,31 @@ class SplitDateTimeField(MultiValueField): # Raise a validation error if time or date is empty # (possible if SplitDateTimeField has required=False). if data_list[0] in self.empty_values: - raise ValidationError(self.error_messages['invalid_date'], code='invalid_date') + raise ValidationError( + self.error_messages["invalid_date"], code="invalid_date" + ) if data_list[1] in self.empty_values: - raise ValidationError(self.error_messages['invalid_time'], code='invalid_time') + raise ValidationError( + self.error_messages["invalid_time"], code="invalid_time" + ) result = datetime.datetime.combine(*data_list) return from_current_timezone(result) return None class GenericIPAddressField(CharField): - def __init__(self, *, protocol='both', unpack_ipv4=False, **kwargs): + def __init__(self, *, protocol="both", unpack_ipv4=False, **kwargs): self.unpack_ipv4 = unpack_ipv4 - self.default_validators = validators.ip_address_validators(protocol, unpack_ipv4)[0] + self.default_validators = validators.ip_address_validators( + protocol, unpack_ipv4 + )[0] super().__init__(**kwargs) def to_python(self, value): if value in self.empty_values: - return '' + return "" value = value.strip() - if value and ':' in value: + if value and ":" in value: return clean_ipv6_address(value, self.unpack_ipv4) return value @@ -1200,7 +1298,7 @@ class SlugField(CharField): class UUIDField(CharField): default_error_messages = { - 'invalid': _('Enter a valid UUID.'), + "invalid": _("Enter a valid UUID."), } def prepare_value(self, value): @@ -1216,7 +1314,7 @@ class UUIDField(CharField): try: value = uuid.UUID(value) except ValueError: - raise ValidationError(self.error_messages['invalid'], code='invalid') + raise ValidationError(self.error_messages["invalid"], code="invalid") return value @@ -1230,7 +1328,7 @@ class JSONString(str): class JSONField(CharField): default_error_messages = { - 'invalid': _('Enter a valid JSON.'), + "invalid": _("Enter a valid JSON."), } widget = Textarea @@ -1250,9 +1348,9 @@ class JSONField(CharField): converted = json.loads(value, cls=self.decoder) except json.JSONDecodeError: raise ValidationError( - self.error_messages['invalid'], - code='invalid', - params={'value': value}, + self.error_messages["invalid"], + code="invalid", + params={"value": value}, ) if isinstance(converted, str): return JSONString(converted) @@ -1279,7 +1377,6 @@ class JSONField(CharField): return True # For purposes of seeing whether something has changed, True isn't the # same as 1 and the order of keys doesn't matter. - return ( - json.dumps(initial, sort_keys=True, cls=self.encoder) != - json.dumps(self.to_python(data), sort_keys=True, cls=self.encoder) + return json.dumps(initial, sort_keys=True, cls=self.encoder) != json.dumps( + self.to_python(data), sort_keys=True, cls=self.encoder ) |
