1"""
2Field classes.
3"""
4
5import copy
6import datetime
7import json
8import math
9import operator
10import os
11import re
12import uuid
13from decimal import Decimal, DecimalException
14from io import BytesIO
15from urllib.parse import urlsplit, urlunsplit
16
17from django.core import validators
18from django.core.exceptions import ValidationError
19from django.forms.boundfield import BoundField
20from django.forms.utils import from_current_timezone, to_current_timezone
21from django.forms.widgets import (
22 FILE_INPUT_CONTRADICTION,
23 CheckboxInput,
24 ClearableFileInput,
25 DateInput,
26 DateTimeInput,
27 EmailInput,
28 FileInput,
29 HiddenInput,
30 MultipleHiddenInput,
31 NullBooleanSelect,
32 NumberInput,
33 Select,
34 SelectMultiple,
35 SplitDateTimeWidget,
36 SplitHiddenDateTimeWidget,
37 Textarea,
38 TextInput,
39 TimeInput,
40 URLInput,
41)
42from django.utils import formats
43from django.utils.choices import normalize_choices
44from django.utils.dateparse import parse_datetime, parse_duration
45from django.utils.duration import duration_string
46from django.utils.ipv6 import MAX_IPV6_ADDRESS_LENGTH, clean_ipv6_address
47from django.utils.regex_helper import _lazy_re_compile
48from django.utils.translation import gettext_lazy as _
49from django.utils.translation import ngettext_lazy
50
51__all__ = (
52 "Field",
53 "CharField",
54 "IntegerField",
55 "DateField",
56 "TimeField",
57 "DateTimeField",
58 "DurationField",
59 "RegexField",
60 "EmailField",
61 "FileField",
62 "ImageField",
63 "URLField",
64 "BooleanField",
65 "NullBooleanField",
66 "ChoiceField",
67 "MultipleChoiceField",
68 "ComboField",
69 "MultiValueField",
70 "FloatField",
71 "DecimalField",
72 "SplitDateTimeField",
73 "GenericIPAddressField",
74 "FilePathField",
75 "JSONField",
76 "SlugField",
77 "TypedChoiceField",
78 "TypedMultipleChoiceField",
79 "UUIDField",
80)
81
82
83class Field:
84 widget = TextInput # Default widget to use when rendering this type of Field.
85 hidden_widget = (
86 HiddenInput # Default widget to use when rendering this as "hidden".
87 )
88 default_validators = [] # Default set of validators
89 # Add an 'invalid' entry to default_error_message if you want a specific
90 # field error message not raised by the field validators.
91 default_error_messages = {
92 "required": _("This field is required."),
93 }
94 empty_values = list(validators.EMPTY_VALUES)
95 bound_field_class = None
96
97 def __init__(
98 self,
99 *,
100 required=True,
101 widget=None,
102 label=None,
103 initial=None,
104 help_text="",
105 error_messages=None,
106 show_hidden_initial=False,
107 validators=(),
108 localize=False,
109 disabled=False,
110 label_suffix=None,
111 template_name=None,
112 bound_field_class=None,
113 ):
114 # required -- Boolean that specifies whether the field is required.
115 # True by default.
116 # widget -- A Widget class, or instance of a Widget class, that should
117 # be used for this Field when displaying it. Each Field has a
118 # default Widget that it'll use if you don't specify this. In
119 # most cases, the default widget is TextInput.
120 # label -- A verbose name for this field, for use in displaying this
121 # field in a form. By default, Django will use a "pretty"
122 # version of the form field name, if the Field is part of a
123 # Form.
124 # initial -- A value to use in this Field's initial display. This value
125 # is *not* used as a fallback if data isn't given.
126 # help_text -- An optional string to use as "help text" for this Field.
127 # error_messages -- An optional dictionary to override the default
128 # messages that the field will raise.
129 # show_hidden_initial -- Boolean that specifies if it is needed to render a
130 # hidden widget with initial value after widget.
131 # validators -- List of additional validators to use
132 # localize -- Boolean that specifies if the field should be localized.
133 # disabled -- Boolean that specifies whether the field is disabled, that
134 # is its widget is shown in the form but not editable.
135 # label_suffix -- Suffix to be added to the label. Overrides
136 # form's label_suffix.
137 # bound_field_class -- BoundField class to use in Field.get_bound_field.
138 self.required, self.label, self.initial = required, label, initial
139 self.show_hidden_initial = show_hidden_initial
140 self.help_text = help_text
141 self.disabled = disabled
142 self.label_suffix = label_suffix
143 self.bound_field_class = bound_field_class or self.bound_field_class
144 widget = widget or self.widget
145 if isinstance(widget, type):
146 widget = widget()
147 else:
148 widget = copy.deepcopy(widget)
149
150 # Trigger the localization machinery if needed.
151 self.localize = localize
152 if self.localize:
153 widget.is_localized = True
154
155 # Let the widget know whether it should display as required.
156 widget.is_required = self.required
157
158 # Hook into self.widget_attrs() for any Field-specific HTML attributes.
159 extra_attrs = self.widget_attrs(widget)
160 if extra_attrs:
161 widget.attrs.update(extra_attrs)
162
163 self.widget = widget
164
165 messages = {}
166 for c in reversed(self.__class__.__mro__):
167 messages.update(getattr(c, "default_error_messages", {}))
168 messages.update(error_messages or {})
169 self.error_messages = messages
170
171 self.validators = [*self.default_validators, *validators]
172 self.template_name = template_name
173
174 super().__init__()
175
176 def prepare_value(self, value):
177 return value
178
179 def to_python(self, value):
180 return value
181
182 def validate(self, value):
183 if value in self.empty_values and self.required:
184 raise ValidationError(self.error_messages["required"], code="required")
185
186 def run_validators(self, value):
187 if value in self.empty_values:
188 return
189 errors = []
190 for v in self.validators:
191 try:
192 v(value)
193 except ValidationError as e:
194 if hasattr(e, "code") and e.code in self.error_messages:
195 e.message = self.error_messages[e.code]
196 errors.extend(e.error_list)
197 if errors:
198 raise ValidationError(errors)
199
200 def clean(self, value):
201 """
202 Validate the given value and return its "cleaned" value as an
203 appropriate Python object. Raise ValidationError for any errors.
204 """
205 value = self.to_python(value)
206 self.validate(value)
207 self.run_validators(value)
208 return value
209
210 def bound_data(self, data, initial):
211 """
212 Return the value that should be shown for this field on render of a
213 bound form, given the submitted POST data for the field and the initial
214 data, if any.
215
216 For most fields, this will simply be data; FileFields need to handle it
217 a bit differently.
218 """
219 if self.disabled:
220 return initial
221 return data
222
223 def widget_attrs(self, widget):
224 """
225 Given a Widget instance (*not* a Widget class), return a dictionary of
226 any HTML attributes that should be added to the Widget, based on this
227 Field.
228 """
229 return {}
230
231 def has_changed(self, initial, data):
232 """Return True if data differs from initial."""
233 # Always return False if the field is disabled since self.bound_data
234 # always uses the initial value in this case.
235 if self.disabled:
236 return False
237 try:
238 data = self.to_python(data)
239 if hasattr(self, "_coerce"):
240 return self._coerce(data) != self._coerce(initial)
241 except ValidationError:
242 return True
243 # For purposes of seeing whether something has changed, None is
244 # the same as an empty string, if the data or initial value we get
245 # is None, replace it with ''.
246 initial_value = initial if initial is not None else ""
247 data_value = data if data is not None else ""
248 return initial_value != data_value
249
250 def get_bound_field(self, form, field_name):
251 """
252 Return a BoundField instance that will be used when accessing the form
253 field in a template.
254 """
255 bound_field_class = (
256 self.bound_field_class or form.bound_field_class or BoundField
257 )
258 return bound_field_class(form, self, field_name)
259
260 def __deepcopy__(self, memo):
261 result = copy.copy(self)
262 memo[id(self)] = result
263 result.widget = copy.deepcopy(self.widget, memo)
264 result.error_messages = self.error_messages.copy()
265 result.validators = self.validators[:]
266 return result
267
268 def _clean_bound_field(self, bf):
269 value = bf.initial if self.disabled else bf.data
270 return self.clean(value)
271
272
273class CharField(Field):
274 def __init__(
275 self, *, max_length=None, min_length=None, strip=True, empty_value="", **kwargs
276 ):
277 self.max_length = max_length
278 self.min_length = min_length
279 self.strip = strip
280 self.empty_value = empty_value
281 super().__init__(**kwargs)
282 if min_length is not None:
283 self.validators.append(validators.MinLengthValidator(int(min_length)))
284 if max_length is not None:
285 self.validators.append(validators.MaxLengthValidator(int(max_length)))
286 self.validators.append(validators.ProhibitNullCharactersValidator())
287
288 def to_python(self, value):
289 """Return a string."""
290 if value not in self.empty_values:
291 value = str(value)
292 if self.strip:
293 value = value.strip()
294 if value in self.empty_values:
295 return self.empty_value
296 return value
297
298 def widget_attrs(self, widget):
299 attrs = super().widget_attrs(widget)
300 if self.max_length is not None and not widget.is_hidden:
301 # The HTML attribute is maxlength, not max_length.
302 attrs["maxlength"] = str(self.max_length)
303 if self.min_length is not None and not widget.is_hidden:
304 # The HTML attribute is minlength, not min_length.
305 attrs["minlength"] = str(self.min_length)
306 return attrs
307
308
309class IntegerField(Field):
310 widget = NumberInput
311 default_error_messages = {
312 "invalid": _("Enter a whole number."),
313 }
314 re_decimal = _lazy_re_compile(r"\.0*\s*$")
315
316 def __init__(self, *, max_value=None, min_value=None, step_size=None, **kwargs):
317 self.max_value, self.min_value, self.step_size = max_value, min_value, step_size
318 if kwargs.get("localize") and self.widget == NumberInput:
319 # Localized number input is not well supported on most browsers
320 kwargs.setdefault("widget", super().widget)
321 super().__init__(**kwargs)
322
323 if max_value is not None:
324 self.validators.append(validators.MaxValueValidator(max_value))
325 if min_value is not None:
326 self.validators.append(validators.MinValueValidator(min_value))
327 if step_size is not None:
328 self.validators.append(
329 validators.StepValueValidator(step_size, offset=min_value)
330 )
331
332 def to_python(self, value):
333 """
334 Validate that int() can be called on the input. Return the result
335 of int() or None for empty values.
336 """
337 value = super().to_python(value)
338 if value in self.empty_values:
339 return None
340 if self.localize:
341 value = formats.sanitize_separators(value)
342 # Strip trailing decimal and zeros.
343 try:
344 value = int(self.re_decimal.sub("", str(value)))
345 except (ValueError, TypeError):
346 raise ValidationError(self.error_messages["invalid"], code="invalid")
347 return value
348
349 def widget_attrs(self, widget):
350 attrs = super().widget_attrs(widget)
351 if isinstance(widget, NumberInput):
352 if self.min_value is not None:
353 attrs["min"] = self.min_value
354 if self.max_value is not None:
355 attrs["max"] = self.max_value
356 if self.step_size is not None:
357 attrs["step"] = self.step_size
358 return attrs
359
360
361class FloatField(IntegerField):
362 default_error_messages = {
363 "invalid": _("Enter a number."),
364 }
365
366 def to_python(self, value):
367 """
368 Validate that float() can be called on the input. Return the result
369 of float() or None for empty values.
370 """
371 value = super(IntegerField, self).to_python(value)
372 if value in self.empty_values:
373 return None
374 if self.localize:
375 value = formats.sanitize_separators(value)
376 try:
377 value = float(value)
378 except (ValueError, TypeError):
379 raise ValidationError(self.error_messages["invalid"], code="invalid")
380 return value
381
382 def validate(self, value):
383 super().validate(value)
384 if value in self.empty_values:
385 return
386 if not math.isfinite(value):
387 raise ValidationError(self.error_messages["invalid"], code="invalid")
388
389 def widget_attrs(self, widget):
390 attrs = super().widget_attrs(widget)
391 if isinstance(widget, NumberInput) and "step" not in widget.attrs:
392 if self.step_size is not None:
393 step = str(self.step_size)
394 else:
395 step = "any"
396 attrs.setdefault("step", step)
397 return attrs
398
399
400class DecimalField(IntegerField):
401 default_error_messages = {
402 "invalid": _("Enter a number."),
403 }
404
405 def __init__(
406 self,
407 *,
408 max_value=None,
409 min_value=None,
410 max_digits=None,
411 decimal_places=None,
412 **kwargs,
413 ):
414 self.max_digits, self.decimal_places = max_digits, decimal_places
415 super().__init__(max_value=max_value, min_value=min_value, **kwargs)
416 self.validators.append(validators.DecimalValidator(max_digits, decimal_places))
417
418 def to_python(self, value):
419 """
420 Validate that the input is a decimal number. Return a Decimal
421 instance or None for empty values. Ensure that there are no more
422 than max_digits in the number and no more than decimal_places digits
423 after the decimal point.
424 """
425 if value in self.empty_values:
426 return None
427 if self.localize:
428 value = formats.sanitize_separators(value)
429 try:
430 value = Decimal(str(value))
431 except DecimalException:
432 raise ValidationError(self.error_messages["invalid"], code="invalid")
433 return value
434
435 def validate(self, value):
436 super().validate(value)
437 if value in self.empty_values:
438 return
439 if not value.is_finite():
440 raise ValidationError(
441 self.error_messages["invalid"],
442 code="invalid",
443 params={"value": value},
444 )
445
446 def widget_attrs(self, widget):
447 attrs = super().widget_attrs(widget)
448 if isinstance(widget, NumberInput) and "step" not in widget.attrs:
449 if self.decimal_places is not None:
450 # Use exponential notation for small values since they might
451 # be parsed as 0 otherwise. ref #20765
452 step = str(Decimal(1).scaleb(-self.decimal_places)).lower()
453 else:
454 step = "any"
455 attrs.setdefault("step", step)
456 return attrs
457
458
459class BaseTemporalField(Field):
460 def __init__(self, *, input_formats=None, **kwargs):
461 super().__init__(**kwargs)
462 if input_formats is not None:
463 self.input_formats = input_formats
464
465 def to_python(self, value):
466 value = value.strip()
467 # Try to strptime against each input format.
468 for format in self.input_formats:
469 try:
470 return self.strptime(value, format)
471 except (ValueError, TypeError):
472 continue
473 raise ValidationError(self.error_messages["invalid"], code="invalid")
474
475 def strptime(self, value, format):
476 raise NotImplementedError("Subclasses must define this method.")
477
478
479class DateField(BaseTemporalField):
480 widget = DateInput
481 input_formats = formats.get_format_lazy("DATE_INPUT_FORMATS")
482 default_error_messages = {
483 "invalid": _("Enter a valid date."),
484 }
485
486 def to_python(self, value):
487 """
488 Validate that the input can be converted to a date. Return a Python
489 datetime.date object.
490 """
491 if value in self.empty_values:
492 return None
493 if isinstance(value, datetime.datetime):
494 return value.date()
495 if isinstance(value, datetime.date):
496 return value
497 return super().to_python(value)
498
499 def strptime(self, value, format):
500 return datetime.datetime.strptime(value, format).date()
501
502
503class TimeField(BaseTemporalField):
504 widget = TimeInput
505 input_formats = formats.get_format_lazy("TIME_INPUT_FORMATS")
506 default_error_messages = {"invalid": _("Enter a valid time.")}
507
508 def to_python(self, value):
509 """
510 Validate that the input can be converted to a time. Return a Python
511 datetime.time object.
512 """
513 if value in self.empty_values:
514 return None
515 if isinstance(value, datetime.time):
516 return value
517 return super().to_python(value)
518
519 def strptime(self, value, format):
520 return datetime.datetime.strptime(value, format).time()
521
522
523class DateTimeFormatsIterator:
524 def __iter__(self):
525 yield from formats.get_format("DATETIME_INPUT_FORMATS")
526 yield from formats.get_format("DATE_INPUT_FORMATS")
527
528
529class DateTimeField(BaseTemporalField):
530 widget = DateTimeInput
531 input_formats = DateTimeFormatsIterator()
532 default_error_messages = {
533 "invalid": _("Enter a valid date/time."),
534 }
535
536 def prepare_value(self, value):
537 if isinstance(value, datetime.datetime):
538 value = to_current_timezone(value)
539 return value
540
541 def to_python(self, value):
542 """
543 Validate that the input can be converted to a datetime. Return a
544 Python datetime.datetime object.
545 """
546 if value in self.empty_values:
547 return None
548 if isinstance(value, datetime.datetime):
549 return from_current_timezone(value)
550 if isinstance(value, datetime.date):
551 result = datetime.datetime(value.year, value.month, value.day)
552 return from_current_timezone(result)
553 try:
554 result = parse_datetime(value.strip())
555 except ValueError:
556 raise ValidationError(self.error_messages["invalid"], code="invalid")
557 if not result:
558 result = super().to_python(value)
559 return from_current_timezone(result)
560
561 def strptime(self, value, format):
562 return datetime.datetime.strptime(value, format)
563
564
565class DurationField(Field):
566 default_error_messages = {
567 "invalid": _("Enter a valid duration."),
568 "overflow": _("The number of days must be between {min_days} and {max_days}."),
569 }
570
571 def prepare_value(self, value):
572 if isinstance(value, datetime.timedelta):
573 return duration_string(value)
574 return value
575
576 def to_python(self, value):
577 if value in self.empty_values:
578 return None
579 if isinstance(value, datetime.timedelta):
580 return value
581 try:
582 value = parse_duration(str(value))
583 except OverflowError:
584 raise ValidationError(
585 self.error_messages["overflow"].format(
586 min_days=datetime.timedelta.min.days,
587 max_days=datetime.timedelta.max.days,
588 ),
589 code="overflow",
590 )
591 if value is None:
592 raise ValidationError(self.error_messages["invalid"], code="invalid")
593 return value
594
595
596class RegexField(CharField):
597 def __init__(self, regex, **kwargs):
598 """
599 regex can be either a string or a compiled regular expression object.
600 """
601 kwargs.setdefault("strip", False)
602 super().__init__(**kwargs)
603 self._set_regex(regex)
604
605 def _get_regex(self):
606 return self._regex
607
608 def _set_regex(self, regex):
609 if isinstance(regex, str):
610 regex = re.compile(regex)
611 self._regex = regex
612 if (
613 hasattr(self, "_regex_validator")
614 and self._regex_validator in self.validators
615 ):
616 self.validators.remove(self._regex_validator)
617 self._regex_validator = validators.RegexValidator(regex=regex)
618 self.validators.append(self._regex_validator)
619
620 regex = property(_get_regex, _set_regex)
621
622
623class EmailField(CharField):
624 widget = EmailInput
625 default_validators = [validators.validate_email]
626
627 def __init__(self, **kwargs):
628 # The default maximum length of an email is 320 characters per RFC 3696
629 # section 3.
630 kwargs.setdefault("max_length", 320)
631 super().__init__(strip=True, **kwargs)
632
633
634class FileField(Field):
635 widget = ClearableFileInput
636 default_error_messages = {
637 "invalid": _("No file was submitted. Check the encoding type on the form."),
638 "missing": _("No file was submitted."),
639 "empty": _("The submitted file is empty."),
640 "max_length": ngettext_lazy(
641 "Ensure this filename has at most %(max)d character (it has %(length)d).",
642 "Ensure this filename has at most %(max)d characters (it has %(length)d).",
643 "max",
644 ),
645 "contradiction": _(
646 "Please either submit a file or check the clear checkbox, not both."
647 ),
648 }
649
650 def __init__(self, *, max_length=None, allow_empty_file=False, **kwargs):
651 self.max_length = max_length
652 self.allow_empty_file = allow_empty_file
653 super().__init__(**kwargs)
654
655 def to_python(self, data):
656 if data in self.empty_values:
657 return None
658
659 # UploadedFile objects should have name and size attributes.
660 try:
661 file_name = data.name
662 file_size = data.size
663 except AttributeError:
664 raise ValidationError(self.error_messages["invalid"], code="invalid")
665
666 if self.max_length is not None and len(file_name) > self.max_length:
667 params = {"max": self.max_length, "length": len(file_name)}
668 raise ValidationError(
669 self.error_messages["max_length"], code="max_length", params=params
670 )
671 if not file_name:
672 raise ValidationError(self.error_messages["invalid"], code="invalid")
673 if not self.allow_empty_file and not file_size:
674 raise ValidationError(self.error_messages["empty"], code="empty")
675
676 return data
677
678 def clean(self, data, initial=None):
679 # If the widget got contradictory inputs, we raise a validation error
680 if data is FILE_INPUT_CONTRADICTION:
681 raise ValidationError(
682 self.error_messages["contradiction"], code="contradiction"
683 )
684 # False means the field value should be cleared; further validation is
685 # not needed.
686 if data is False:
687 if not self.required:
688 return False
689 # If the field is required, clearing is not possible (the widget
690 # shouldn't return False data in that case anyway). False is not
691 # in self.empty_value; if a False value makes it this far
692 # it should be validated from here on out as None (so it will be
693 # caught by the required check).
694 data = None
695 if not data and initial:
696 return initial
697 return super().clean(data)
698
699 def bound_data(self, _, initial):
700 return initial
701
702 def has_changed(self, initial, data):
703 return not self.disabled and data is not None
704
705 def _clean_bound_field(self, bf):
706 value = bf.initial if self.disabled else bf.data
707 return self.clean(value, bf.initial)
708
709
710class ImageField(FileField):
711 default_validators = [validators.validate_image_file_extension]
712 default_error_messages = {
713 "invalid_image": _(
714 "Upload a valid image. The file you uploaded was either not an "
715 "image or a corrupted image."
716 ),
717 }
718
719 def to_python(self, data):
720 """
721 Check that the file-upload field data contains a valid image (GIF, JPG,
722 PNG, etc. -- whatever Pillow supports).
723 """
724 f = super().to_python(data)
725 if f is None:
726 return None
727
728 from PIL import Image
729
730 # We need to get a file object for Pillow. We might have a path or we might
731 # have to read the data into memory.
732 if hasattr(data, "temporary_file_path"):
733 file = data.temporary_file_path()
734 else:
735 if hasattr(data, "read"):
736 file = BytesIO(data.read())
737 else:
738 file = BytesIO(data["content"])
739
740 try:
741 # load() could spot a truncated JPEG, but it loads the entire
742 # image in memory, which is a DoS vector. See #3848 and #18520.
743 image = Image.open(file)
744 # verify() must be called immediately after the constructor.
745 image.verify()
746
747 # Annotating so subclasses can reuse it for their own validation
748 f.image = image
749 # Pillow doesn't detect the MIME type of all formats. In those
750 # cases, content_type will be None.
751 f.content_type = Image.MIME.get(image.format)
752 except Exception as exc:
753 # Pillow doesn't recognize it as an image.
754 raise ValidationError(
755 self.error_messages["invalid_image"],
756 code="invalid_image",
757 ) from exc
758 if hasattr(f, "seek") and callable(f.seek):
759 f.seek(0)
760 return f
761
762 def widget_attrs(self, widget):
763 attrs = super().widget_attrs(widget)
764 if isinstance(widget, FileInput) and "accept" not in widget.attrs:
765 attrs.setdefault("accept", "image/*")
766 return attrs
767
768
769class URLField(CharField):
770 widget = URLInput
771 default_error_messages = {
772 "invalid": _("Enter a valid URL."),
773 }
774 default_validators = [validators.URLValidator()]
775
776 def __init__(self, *, assume_scheme=None, **kwargs):
777 self.assume_scheme = assume_scheme or "https"
778 super().__init__(strip=True, **kwargs)
779
780 def to_python(self, value):
781 def split_url(url):
782 """
783 Return a list of url parts via urlsplit(), or raise
784 ValidationError for some malformed URLs.
785 """
786 try:
787 return list(urlsplit(url))
788 except ValueError:
789 # urlsplit can raise a ValueError with some
790 # misformatted URLs.
791 raise ValidationError(self.error_messages["invalid"], code="invalid")
792
793 value = super().to_python(value)
794 if value:
795 url_fields = split_url(value)
796 if not url_fields[0]:
797 # If no URL scheme given, add a scheme.
798 url_fields[0] = self.assume_scheme
799 if not url_fields[1]:
800 # Assume that if no domain is provided, that the path segment
801 # contains the domain.
802 url_fields[1] = url_fields[2]
803 url_fields[2] = ""
804 # Rebuild the url_fields list, since the domain segment may now
805 # contain the path too.
806 url_fields = split_url(urlunsplit(url_fields))
807 value = urlunsplit(url_fields)
808 return value
809
810
811class BooleanField(Field):
812 widget = CheckboxInput
813
814 def to_python(self, value):
815 """Return a Python boolean object."""
816 # Explicitly check for the string 'False', which is what a hidden field
817 # will submit for False. Also check for '0', since this is what
818 # RadioSelect will provide. Because bool("True") == bool('1') == True,
819 # we don't need to handle that explicitly.
820 if isinstance(value, str) and value.lower() in ("false", "0"):
821 value = False
822 else:
823 value = bool(value)
824 return super().to_python(value)
825
826 def validate(self, value):
827 if not value and self.required:
828 raise ValidationError(self.error_messages["required"], code="required")
829
830 def has_changed(self, initial, data):
831 if self.disabled:
832 return False
833 # Sometimes data or initial may be a string equivalent of a boolean
834 # so we should run it through to_python first to get a boolean value
835 return self.to_python(initial) != self.to_python(data)
836
837
838class NullBooleanField(BooleanField):
839 """
840 A field whose valid values are None, True, and False. Clean invalid values
841 to None.
842 """
843
844 widget = NullBooleanSelect
845
846 def to_python(self, value):
847 """
848 Explicitly check for the string 'True' and 'False', which is what a
849 hidden field will submit for True and False, for 'true' and 'false',
850 which are likely to be returned by JavaScript serializations of forms,
851 and for '1' and '0', which is what a RadioField will submit. Unlike
852 the Booleanfield, this field must check for True because it doesn't
853 use the bool() function.
854 """
855 if value in (True, "True", "true", "1"):
856 return True
857 elif value in (False, "False", "false", "0"):
858 return False
859 else:
860 return None
861
862 def validate(self, value):
863 pass
864
865
866class ChoiceField(Field):
867 widget = Select
868 default_error_messages = {
869 "invalid_choice": _(
870 "Select a valid choice. %(value)s is not one of the available choices."
871 ),
872 }
873
874 def __init__(self, *, choices=(), **kwargs):
875 super().__init__(**kwargs)
876 self.choices = choices
877
878 def __deepcopy__(self, memo):
879 result = super().__deepcopy__(memo)
880 result._choices = copy.deepcopy(self._choices, memo)
881 return result
882
883 @property
884 def choices(self):
885 return self._choices
886
887 @choices.setter
888 def choices(self, value):
889 # Setting choices on the field also sets the choices on the widget.
890 # Note that the property setter for the widget will re-normalize.
891 self._choices = self.widget.choices = normalize_choices(value)
892
893 def to_python(self, value):
894 """Return a string."""
895 if value in self.empty_values:
896 return ""
897 return str(value)
898
899 def validate(self, value):
900 """Validate that the input is in self.choices."""
901 super().validate(value)
902 if value and not self.valid_value(value):
903 raise ValidationError(
904 self.error_messages["invalid_choice"],
905 code="invalid_choice",
906 params={"value": value},
907 )
908
909 def valid_value(self, value):
910 """Check to see if the provided value is a valid choice."""
911 text_value = str(value)
912 for k, v in self.choices:
913 if isinstance(v, (list, tuple)):
914 # This is an optgroup, so look inside the group for options
915 for k2, v2 in v:
916 if value == k2 or text_value == str(k2):
917 return True
918 else:
919 if value == k or text_value == str(k):
920 return True
921 return False
922
923
924class TypedChoiceField(ChoiceField):
925 def __init__(self, *, coerce=lambda val: val, empty_value="", **kwargs):
926 self.coerce = coerce
927 self.empty_value = empty_value
928 super().__init__(**kwargs)
929
930 def _coerce(self, value):
931 """
932 Validate that the value can be coerced to the right type (if not empty).
933 """
934 if value == self.empty_value or value in self.empty_values:
935 return self.empty_value
936 try:
937 value = self.coerce(value)
938 except (ValueError, TypeError, ValidationError):
939 raise ValidationError(
940 self.error_messages["invalid_choice"],
941 code="invalid_choice",
942 params={"value": value},
943 )
944 return value
945
946 def clean(self, value):
947 value = super().clean(value)
948 return self._coerce(value)
949
950
951class MultipleChoiceField(ChoiceField):
952 hidden_widget = MultipleHiddenInput
953 widget = SelectMultiple
954 default_error_messages = {
955 "invalid_choice": _(
956 "Select a valid choice. %(value)s is not one of the available choices."
957 ),
958 "invalid_list": _("Enter a list of values."),
959 }
960
961 def to_python(self, value):
962 if not value:
963 return []
964 elif not isinstance(value, (list, tuple)):
965 raise ValidationError(
966 self.error_messages["invalid_list"], code="invalid_list"
967 )
968 return [str(val) for val in value]
969
970 def validate(self, value):
971 """Validate that the input is a list or tuple."""
972 if self.required and not value:
973 raise ValidationError(self.error_messages["required"], code="required")
974 # Validate that each value in the value list is in self.choices.
975 for val in value:
976 if not self.valid_value(val):
977 raise ValidationError(
978 self.error_messages["invalid_choice"],
979 code="invalid_choice",
980 params={"value": val},
981 )
982
983 def has_changed(self, initial, data):
984 if self.disabled:
985 return False
986 if initial is None:
987 initial = []
988 if data is None:
989 data = []
990 if len(initial) != len(data):
991 return True
992 initial_set = {str(value) for value in initial}
993 data_set = {str(value) for value in data}
994 return data_set != initial_set
995
996
997class TypedMultipleChoiceField(MultipleChoiceField):
998 def __init__(self, *, coerce=lambda val: val, **kwargs):
999 self.coerce = coerce
1000 self.empty_value = kwargs.pop("empty_value", [])
1001 super().__init__(**kwargs)
1002
1003 def _coerce(self, value):
1004 """
1005 Validate that the values are in self.choices and can be coerced to the
1006 right type.
1007 """
1008 if value == self.empty_value or value in self.empty_values:
1009 return self.empty_value
1010 new_value = []
1011 for choice in value:
1012 try:
1013 new_value.append(self.coerce(choice))
1014 except (ValueError, TypeError, ValidationError):
1015 raise ValidationError(
1016 self.error_messages["invalid_choice"],
1017 code="invalid_choice",
1018 params={"value": choice},
1019 )
1020 return new_value
1021
1022 def clean(self, value):
1023 value = super().clean(value)
1024 return self._coerce(value)
1025
1026 def validate(self, value):
1027 if value != self.empty_value:
1028 super().validate(value)
1029 elif self.required:
1030 raise ValidationError(self.error_messages["required"], code="required")
1031
1032
1033class ComboField(Field):
1034 """
1035 A Field whose clean() method calls multiple Field clean() methods.
1036 """
1037
1038 def __init__(self, fields, **kwargs):
1039 super().__init__(**kwargs)
1040 # Set 'required' to False on the individual fields, because the
1041 # required validation will be handled by ComboField, not by those
1042 # individual fields.
1043 for f in fields:
1044 f.required = False
1045 self.fields = fields
1046
1047 def clean(self, value):
1048 """
1049 Validate the given value against all of self.fields, which is a
1050 list of Field instances.
1051 """
1052 super().clean(value)
1053 for field in self.fields:
1054 value = field.clean(value)
1055 return value
1056
1057
1058class MultiValueField(Field):
1059 """
1060 Aggregate the logic of multiple Fields.
1061
1062 Its clean() method takes a "decompressed" list of values, which are then
1063 cleaned into a single value according to self.fields. Each value in
1064 this list is cleaned by the corresponding field -- the first value is
1065 cleaned by the first field, the second value is cleaned by the second
1066 field, etc. Once all fields are cleaned, the list of clean values is
1067 "compressed" into a single value.
1068
1069 Subclasses should not have to implement clean(). Instead, they must
1070 implement compress(), which takes a list of valid values and returns a
1071 "compressed" version of those values -- a single value.
1072
1073 You'll probably want to use this with MultiWidget.
1074 """
1075
1076 default_error_messages = {
1077 "invalid": _("Enter a list of values."),
1078 "incomplete": _("Enter a complete value."),
1079 }
1080
1081 def __init__(self, fields, *, require_all_fields=True, **kwargs):
1082 self.require_all_fields = require_all_fields
1083 super().__init__(**kwargs)
1084 for f in fields:
1085 f.error_messages.setdefault("incomplete", self.error_messages["incomplete"])
1086 if self.disabled:
1087 f.disabled = True
1088 if self.require_all_fields:
1089 # Set 'required' to False on the individual fields, because the
1090 # required validation will be handled by MultiValueField, not
1091 # by those individual fields.
1092 f.required = False
1093 self.fields = fields
1094
1095 def __deepcopy__(self, memo):
1096 result = super().__deepcopy__(memo)
1097 result.fields = tuple(x.__deepcopy__(memo) for x in self.fields)
1098 return result
1099
1100 def validate(self, value):
1101 pass
1102
1103 def clean(self, value):
1104 """
1105 Validate every value in the given list. A value is validated against
1106 the corresponding Field in self.fields.
1107
1108 For example, if this MultiValueField was instantiated with
1109 fields=(DateField(), TimeField()), clean() would call
1110 DateField.clean(value[0]) and TimeField.clean(value[1]).
1111 """
1112 clean_data = []
1113 errors = []
1114 if self.disabled and not isinstance(value, list):
1115 value = self.widget.decompress(value)
1116 if not value or isinstance(value, (list, tuple)):
1117 if not value or not [v for v in value if v not in self.empty_values]:
1118 if self.required:
1119 raise ValidationError(
1120 self.error_messages["required"], code="required"
1121 )
1122 else:
1123 return self.compress([])
1124 else:
1125 raise ValidationError(self.error_messages["invalid"], code="invalid")
1126 for i, field in enumerate(self.fields):
1127 try:
1128 field_value = value[i]
1129 except IndexError:
1130 field_value = None
1131 if field_value in self.empty_values:
1132 if self.require_all_fields:
1133 # Raise a 'required' error if the MultiValueField is
1134 # required and any field is empty.
1135 if self.required:
1136 raise ValidationError(
1137 self.error_messages["required"], code="required"
1138 )
1139 elif field.required:
1140 # Otherwise, add an 'incomplete' error to the list of
1141 # collected errors and skip field cleaning, if a required
1142 # field is empty.
1143 if field.error_messages["incomplete"] not in errors:
1144 errors.append(field.error_messages["incomplete"])
1145 continue
1146 try:
1147 clean_data.append(field.clean(field_value))
1148 except ValidationError as e:
1149 # Collect all validation errors in a single list, which we'll
1150 # raise at the end of clean(), rather than raising a single
1151 # exception for the first error we encounter. Skip duplicates.
1152 errors.extend(m for m in e.error_list if m not in errors)
1153 if errors:
1154 raise ValidationError(errors)
1155
1156 out = self.compress(clean_data)
1157 self.validate(out)
1158 self.run_validators(out)
1159 return out
1160
1161 def compress(self, data_list):
1162 """
1163 Return a single value for the given list of values. The values can be
1164 assumed to be valid.
1165
1166 For example, if this MultiValueField was instantiated with
1167 fields=(DateField(), TimeField()), this might return a datetime
1168 object created by combining the date and time in data_list.
1169 """
1170 raise NotImplementedError("Subclasses must implement this method.")
1171
1172 def has_changed(self, initial, data):
1173 if self.disabled:
1174 return False
1175 if initial is None:
1176 initial = ["" for x in range(0, len(data))]
1177 else:
1178 if not isinstance(initial, list):
1179 initial = self.widget.decompress(initial)
1180 for field, initial, data in zip(self.fields, initial, data):
1181 try:
1182 initial = field.to_python(initial)
1183 except ValidationError:
1184 return True
1185 if field.has_changed(initial, data):
1186 return True
1187 return False
1188
1189
1190class FilePathField(ChoiceField):
1191 def __init__(
1192 self,
1193 path,
1194 *,
1195 match=None,
1196 recursive=False,
1197 allow_files=True,
1198 allow_folders=False,
1199 **kwargs,
1200 ):
1201 self.path, self.match, self.recursive = path, match, recursive
1202 self.allow_files, self.allow_folders = allow_files, allow_folders
1203 super().__init__(choices=(), **kwargs)
1204
1205 if self.required:
1206 self.choices = []
1207 else:
1208 self.choices = [("", "---------")]
1209
1210 if self.match is not None:
1211 self.match_re = re.compile(self.match)
1212
1213 if recursive:
1214 for root, dirs, files in sorted(os.walk(self.path)):
1215 if self.allow_files:
1216 for f in sorted(files):
1217 if self.match is None or self.match_re.search(f):
1218 f = os.path.join(root, f)
1219 self.choices.append((f, f.replace(path, "", 1)))
1220 if self.allow_folders:
1221 for f in sorted(dirs):
1222 if f == "__pycache__":
1223 continue
1224 if self.match is None or self.match_re.search(f):
1225 f = os.path.join(root, f)
1226 self.choices.append((f, f.replace(path, "", 1)))
1227 else:
1228 choices = []
1229 with os.scandir(self.path) as entries:
1230 for f in entries:
1231 if f.name == "__pycache__":
1232 continue
1233 if (
1234 (self.allow_files and f.is_file())
1235 or (self.allow_folders and f.is_dir())
1236 ) and (self.match is None or self.match_re.search(f.name)):
1237 choices.append((f.path, f.name))
1238 choices.sort(key=operator.itemgetter(1))
1239 self.choices.extend(choices)
1240
1241 self.widget.choices = self.choices
1242
1243
1244class SplitDateTimeField(MultiValueField):
1245 widget = SplitDateTimeWidget
1246 hidden_widget = SplitHiddenDateTimeWidget
1247 default_error_messages = {
1248 "invalid_date": _("Enter a valid date."),
1249 "invalid_time": _("Enter a valid time."),
1250 }
1251
1252 def __init__(self, *, input_date_formats=None, input_time_formats=None, **kwargs):
1253 errors = self.default_error_messages.copy()
1254 if "error_messages" in kwargs:
1255 errors.update(kwargs["error_messages"])
1256 localize = kwargs.get("localize", False)
1257 fields = (
1258 DateField(
1259 input_formats=input_date_formats,
1260 error_messages={"invalid": errors["invalid_date"]},
1261 localize=localize,
1262 ),
1263 TimeField(
1264 input_formats=input_time_formats,
1265 error_messages={"invalid": errors["invalid_time"]},
1266 localize=localize,
1267 ),
1268 )
1269 super().__init__(fields, **kwargs)
1270
1271 def compress(self, data_list):
1272 if data_list:
1273 # Raise a validation error if time or date is empty
1274 # (possible if SplitDateTimeField has required=False).
1275 if data_list[0] in self.empty_values:
1276 raise ValidationError(
1277 self.error_messages["invalid_date"], code="invalid_date"
1278 )
1279 if data_list[1] in self.empty_values:
1280 raise ValidationError(
1281 self.error_messages["invalid_time"], code="invalid_time"
1282 )
1283 result = datetime.datetime.combine(*data_list)
1284 return from_current_timezone(result)
1285 return None
1286
1287
1288class GenericIPAddressField(CharField):
1289 def __init__(self, *, protocol="both", unpack_ipv4=False, **kwargs):
1290 self.unpack_ipv4 = unpack_ipv4
1291 self.default_validators = validators.ip_address_validators(
1292 protocol, unpack_ipv4
1293 )
1294 kwargs.setdefault("max_length", MAX_IPV6_ADDRESS_LENGTH)
1295 super().__init__(**kwargs)
1296
1297 def to_python(self, value):
1298 if value in self.empty_values:
1299 return ""
1300 value = value.strip()
1301 if value and ":" in value:
1302 return clean_ipv6_address(
1303 value, self.unpack_ipv4, max_length=self.max_length
1304 )
1305 return value
1306
1307
1308class SlugField(CharField):
1309 default_validators = [validators.validate_slug]
1310
1311 def __init__(self, *, allow_unicode=False, **kwargs):
1312 self.allow_unicode = allow_unicode
1313 if self.allow_unicode:
1314 self.default_validators = [validators.validate_unicode_slug]
1315 super().__init__(**kwargs)
1316
1317
1318class UUIDField(CharField):
1319 default_error_messages = {
1320 "invalid": _("Enter a valid UUID."),
1321 }
1322
1323 def prepare_value(self, value):
1324 if isinstance(value, uuid.UUID):
1325 return str(value)
1326 return value
1327
1328 def to_python(self, value):
1329 value = super().to_python(value)
1330 if value in self.empty_values:
1331 return None
1332 if not isinstance(value, uuid.UUID):
1333 try:
1334 value = uuid.UUID(value)
1335 except ValueError:
1336 raise ValidationError(self.error_messages["invalid"], code="invalid")
1337 return value
1338
1339
1340class InvalidJSONInput(str):
1341 pass
1342
1343
1344class JSONString(str):
1345 pass
1346
1347
1348class JSONField(CharField):
1349 default_error_messages = {
1350 "invalid": _("Enter a valid JSON."),
1351 }
1352 widget = Textarea
1353
1354 def __init__(self, encoder=None, decoder=None, **kwargs):
1355 self.encoder = encoder
1356 self.decoder = decoder
1357 super().__init__(**kwargs)
1358
1359 def to_python(self, value):
1360 if self.disabled:
1361 return value
1362 if value in self.empty_values:
1363 return None
1364 elif isinstance(value, (list, dict, int, float, JSONString)):
1365 return value
1366 try:
1367 converted = json.loads(value, cls=self.decoder)
1368 except json.JSONDecodeError:
1369 raise ValidationError(
1370 self.error_messages["invalid"],
1371 code="invalid",
1372 params={"value": value},
1373 )
1374 if isinstance(converted, str):
1375 return JSONString(converted)
1376 else:
1377 return converted
1378
1379 def bound_data(self, data, initial):
1380 if self.disabled:
1381 return initial
1382 if data is None:
1383 return None
1384 try:
1385 return json.loads(data, cls=self.decoder)
1386 except json.JSONDecodeError:
1387 return InvalidJSONInput(data)
1388
1389 def prepare_value(self, value):
1390 if isinstance(value, InvalidJSONInput):
1391 return value
1392 return json.dumps(value, ensure_ascii=False, cls=self.encoder)
1393
1394 def has_changed(self, initial, data):
1395 if super().has_changed(initial, data):
1396 return True
1397 # For purposes of seeing whether something has changed, True isn't the
1398 # same as 1 and the order of keys doesn't matter.
1399 return json.dumps(initial, sort_keys=True, cls=self.encoder) != json.dumps(
1400 self.to_python(data), sort_keys=True, cls=self.encoder
1401 )