Plain is headed towards 1.0! Subscribe for development updates →

   1"""
   2Field classes.
   3"""
   4
   5import copy
   6import datetime
   7import enum
   8import json
   9import math
  10import re
  11import uuid
  12from decimal import Decimal, DecimalException
  13from io import BytesIO
  14from urllib.parse import urlsplit, urlunsplit
  15
  16from plain import validators
  17from plain.exceptions import ValidationError
  18from plain.utils import timezone
  19from plain.utils.dateparse import parse_datetime, parse_duration
  20from plain.utils.duration import duration_string
  21from plain.utils.regex_helper import _lazy_re_compile
  22from plain.utils.text import pluralize_lazy
  23
  24from .boundfield import BoundField
  25from .exceptions import FormFieldMissingError
  26
  27__all__ = (
  28    "Field",
  29    "CharField",
  30    "IntegerField",
  31    "DateField",
  32    "TimeField",
  33    "DateTimeField",
  34    "DurationField",
  35    "RegexField",
  36    "EmailField",
  37    "FileField",
  38    "ImageField",
  39    "URLField",
  40    "BooleanField",
  41    "NullBooleanField",
  42    "ChoiceField",
  43    "MultipleChoiceField",
  44    "FloatField",
  45    "DecimalField",
  46    "JSONField",
  47    "TypedChoiceField",
  48    "UUIDField",
  49)
  50
  51
  52FILE_INPUT_CONTRADICTION = object()
  53
  54
  55class Field:
  56    default_validators = []  # Default set of validators
  57    # Add an 'invalid' entry to default_error_message if you want a specific
  58    # field error message not raised by the field validators.
  59    default_error_messages = {
  60        "required": "This field is required.",
  61    }
  62    empty_values = list(validators.EMPTY_VALUES)
  63
  64    def __init__(
  65        self,
  66        *,
  67        required=True,
  68        initial=None,
  69        error_messages=None,
  70        validators=(),
  71    ):
  72        # required -- Boolean that specifies whether the field is required.
  73        #             True by default.
  74        # initial -- A value to use in this Field's initial display. This value
  75        #            is *not* used as a fallback if data isn't given.
  76        # error_messages -- An optional dictionary to override the default
  77        #                   messages that the field will raise.
  78        # validators -- List of additional validators to use
  79        self.required = required
  80        self.initial = initial
  81
  82        messages = {}
  83        for c in reversed(self.__class__.__mro__):
  84            messages.update(getattr(c, "default_error_messages", {}))
  85        messages.update(error_messages or {})
  86        self.error_messages = messages
  87
  88        self.validators = [*self.default_validators, *validators]
  89
  90    def prepare_value(self, value):
  91        return value
  92
  93    def to_python(self, value):
  94        return value
  95
  96    def validate(self, value):
  97        if value in self.empty_values and self.required:
  98            raise ValidationError(self.error_messages["required"], code="required")
  99
 100    def run_validators(self, value):
 101        if value in self.empty_values:
 102            return
 103        errors = []
 104        for v in self.validators:
 105            try:
 106                v(value)
 107            except ValidationError as e:
 108                if hasattr(e, "code") and e.code in self.error_messages:
 109                    e.message = self.error_messages[e.code]
 110                errors.extend(e.error_list)
 111        if errors:
 112            raise ValidationError(errors)
 113
 114    def clean(self, value):
 115        """
 116        Validate the given value and return its "cleaned" value as an
 117        appropriate Python object. Raise ValidationError for any errors.
 118        """
 119        value = self.to_python(value)
 120        self.validate(value)
 121        self.run_validators(value)
 122        return value
 123
 124    def bound_data(self, data, initial):
 125        """
 126        Return the value that should be shown for this field on render of a
 127        bound form, given the submitted POST data for the field and the initial
 128        data, if any.
 129
 130        For most fields, this will simply be data; FileFields need to handle it
 131        a bit differently.
 132        """
 133        return data
 134
 135    def has_changed(self, initial, data):
 136        """Return True if data differs from initial."""
 137        try:
 138            data = self.to_python(data)
 139            if hasattr(self, "_coerce"):
 140                return self._coerce(data) != self._coerce(initial)
 141        except ValidationError:
 142            return True
 143        # For purposes of seeing whether something has changed, None is
 144        # the same as an empty string, if the data or initial value we get
 145        # is None, replace it with ''.
 146        initial_value = initial if initial is not None else ""
 147        data_value = data if data is not None else ""
 148        return initial_value != data_value
 149
 150    def get_bound_field(self, form, field_name):
 151        """
 152        Return a BoundField instance that will be used when accessing the form
 153        field in a template.
 154        """
 155        return BoundField(form, self, field_name)
 156
 157    def __deepcopy__(self, memo):
 158        result = copy.copy(self)
 159        memo[id(self)] = result
 160        result.error_messages = self.error_messages.copy()
 161        result.validators = self.validators[:]
 162        return result
 163
 164    def value_from_form_data(self, data, files, html_name):
 165        # By default, all fields are expected to be present in HTML form data.
 166        try:
 167            return data[html_name]
 168        except KeyError as e:
 169            raise FormFieldMissingError(html_name) from e
 170
 171    def value_from_json_data(self, data, files, html_name):
 172        if self.required and html_name not in data:
 173            raise FormFieldMissingError(html_name)
 174
 175        return data.get(html_name, None)
 176
 177
 178class CharField(Field):
 179    def __init__(
 180        self, *, max_length=None, min_length=None, strip=True, empty_value="", **kwargs
 181    ):
 182        self.max_length = max_length
 183        self.min_length = min_length
 184        self.strip = strip
 185        self.empty_value = empty_value
 186        super().__init__(**kwargs)
 187        if min_length is not None:
 188            self.validators.append(validators.MinLengthValidator(int(min_length)))
 189        if max_length is not None:
 190            self.validators.append(validators.MaxLengthValidator(int(max_length)))
 191        self.validators.append(validators.ProhibitNullCharactersValidator())
 192
 193    def to_python(self, value):
 194        """Return a string."""
 195        if value not in self.empty_values:
 196            value = str(value)
 197            if self.strip:
 198                value = value.strip()
 199        if value in self.empty_values:
 200            return self.empty_value
 201        return value
 202
 203
 204class IntegerField(Field):
 205    default_error_messages = {
 206        "invalid": "Enter a whole number.",
 207    }
 208    re_decimal = _lazy_re_compile(r"\.0*\s*$")
 209
 210    def __init__(self, *, max_value=None, min_value=None, step_size=None, **kwargs):
 211        self.max_value, self.min_value, self.step_size = max_value, min_value, step_size
 212        super().__init__(**kwargs)
 213
 214        if max_value is not None:
 215            self.validators.append(validators.MaxValueValidator(max_value))
 216        if min_value is not None:
 217            self.validators.append(validators.MinValueValidator(min_value))
 218        if step_size is not None:
 219            self.validators.append(validators.StepValueValidator(step_size))
 220
 221    def to_python(self, value):
 222        """
 223        Validate that int() can be called on the input. Return the result
 224        of int() or None for empty values.
 225        """
 226        value = super().to_python(value)
 227        if value in self.empty_values:
 228            return None
 229        # Strip trailing decimal and zeros.
 230        try:
 231            value = int(self.re_decimal.sub("", str(value)))
 232        except (ValueError, TypeError):
 233            raise ValidationError(self.error_messages["invalid"], code="invalid")
 234        return value
 235
 236
 237class FloatField(IntegerField):
 238    default_error_messages = {
 239        "invalid": "Enter a number.",
 240    }
 241
 242    def to_python(self, value):
 243        """
 244        Validate that float() can be called on the input. Return the result
 245        of float() or None for empty values.
 246        """
 247        value = super(IntegerField, self).to_python(value)
 248        if value in self.empty_values:
 249            return None
 250        try:
 251            value = float(value)
 252        except (ValueError, TypeError):
 253            raise ValidationError(self.error_messages["invalid"], code="invalid")
 254        return value
 255
 256    def validate(self, value):
 257        super().validate(value)
 258        if value in self.empty_values:
 259            return
 260        if not math.isfinite(value):
 261            raise ValidationError(self.error_messages["invalid"], code="invalid")
 262
 263
 264class DecimalField(IntegerField):
 265    default_error_messages = {
 266        "invalid": "Enter a number.",
 267    }
 268
 269    def __init__(
 270        self,
 271        *,
 272        max_value=None,
 273        min_value=None,
 274        max_digits=None,
 275        decimal_places=None,
 276        **kwargs,
 277    ):
 278        self.max_digits, self.decimal_places = max_digits, decimal_places
 279        super().__init__(max_value=max_value, min_value=min_value, **kwargs)
 280        self.validators.append(validators.DecimalValidator(max_digits, decimal_places))
 281
 282    def to_python(self, value):
 283        """
 284        Validate that the input is a decimal number. Return a Decimal
 285        instance or None for empty values. Ensure that there are no more
 286        than max_digits in the number and no more than decimal_places digits
 287        after the decimal point.
 288        """
 289        if value in self.empty_values:
 290            return None
 291        try:
 292            value = Decimal(str(value))
 293        except DecimalException:
 294            raise ValidationError(self.error_messages["invalid"], code="invalid")
 295        return value
 296
 297    def validate(self, value):
 298        super().validate(value)
 299        if value in self.empty_values:
 300            return
 301        if not value.is_finite():
 302            raise ValidationError(
 303                self.error_messages["invalid"],
 304                code="invalid",
 305                params={"value": value},
 306            )
 307
 308
 309class BaseTemporalField(Field):
 310    # Default formats to be used when parsing dates from input boxes, in order
 311    # See all available format string here:
 312    # https://docs.python.org/library/datetime.html#strftime-behavior
 313    # * Note that these format strings are different from the ones to display dates
 314    DATE_INPUT_FORMATS = [
 315        "%Y-%m-%d",  # '2006-10-25'
 316        "%m/%d/%Y",  # '10/25/2006'
 317        "%m/%d/%y",  # '10/25/06'
 318        "%b %d %Y",  # 'Oct 25 2006'
 319        "%b %d, %Y",  # 'Oct 25, 2006'
 320        "%d %b %Y",  # '25 Oct 2006'
 321        "%d %b, %Y",  # '25 Oct, 2006'
 322        "%B %d %Y",  # 'October 25 2006'
 323        "%B %d, %Y",  # 'October 25, 2006'
 324        "%d %B %Y",  # '25 October 2006'
 325        "%d %B, %Y",  # '25 October, 2006'
 326    ]
 327
 328    # Default formats to be used when parsing times from input boxes, in order
 329    # See all available format string here:
 330    # https://docs.python.org/library/datetime.html#strftime-behavior
 331    # * Note that these format strings are different from the ones to display dates
 332    TIME_INPUT_FORMATS = [
 333        "%H:%M:%S",  # '14:30:59'
 334        "%H:%M:%S.%f",  # '14:30:59.000200'
 335        "%H:%M",  # '14:30'
 336    ]
 337
 338    # Default formats to be used when parsing dates and times from input boxes,
 339    # in order
 340    # See all available format string here:
 341    # https://docs.python.org/library/datetime.html#strftime-behavior
 342    # * Note that these format strings are different from the ones to display dates
 343    DATETIME_INPUT_FORMATS = [
 344        "%Y-%m-%d %H:%M:%S",  # '2006-10-25 14:30:59'
 345        "%Y-%m-%d %H:%M:%S.%f",  # '2006-10-25 14:30:59.000200'
 346        "%Y-%m-%d %H:%M",  # '2006-10-25 14:30'
 347        "%m/%d/%Y %H:%M:%S",  # '10/25/2006 14:30:59'
 348        "%m/%d/%Y %H:%M:%S.%f",  # '10/25/2006 14:30:59.000200'
 349        "%m/%d/%Y %H:%M",  # '10/25/2006 14:30'
 350        "%m/%d/%y %H:%M:%S",  # '10/25/06 14:30:59'
 351        "%m/%d/%y %H:%M:%S.%f",  # '10/25/06 14:30:59.000200'
 352        "%m/%d/%y %H:%M",  # '10/25/06 14:30'
 353    ]
 354
 355    def __init__(self, *, input_formats=None, **kwargs):
 356        super().__init__(**kwargs)
 357        if input_formats is not None:
 358            self.input_formats = input_formats
 359
 360    def to_python(self, value):
 361        value = value.strip()
 362        # Try to strptime against each input format.
 363        for format in self.input_formats:
 364            try:
 365                return self.strptime(value, format)
 366            except (ValueError, TypeError):
 367                continue
 368        raise ValidationError(self.error_messages["invalid"], code="invalid")
 369
 370    def strptime(self, value, format):
 371        raise NotImplementedError("Subclasses must define this method.")
 372
 373
 374class DateField(BaseTemporalField):
 375    input_formats = BaseTemporalField.DATE_INPUT_FORMATS
 376    default_error_messages = {
 377        "invalid": "Enter a valid date.",
 378    }
 379
 380    def to_python(self, value):
 381        """
 382        Validate that the input can be converted to a date. Return a Python
 383        datetime.date object.
 384        """
 385        if value in self.empty_values:
 386            return None
 387        if isinstance(value, datetime.datetime):
 388            return value.date()
 389        if isinstance(value, datetime.date):
 390            return value
 391        return super().to_python(value)
 392
 393    def strptime(self, value, format):
 394        return datetime.datetime.strptime(value, format).date()
 395
 396
 397class TimeField(BaseTemporalField):
 398    input_formats = BaseTemporalField.TIME_INPUT_FORMATS
 399    default_error_messages = {"invalid": "Enter a valid time."}
 400
 401    def to_python(self, value):
 402        """
 403        Validate that the input can be converted to a time. Return a Python
 404        datetime.time object.
 405        """
 406        if value in self.empty_values:
 407            return None
 408        if isinstance(value, datetime.time):
 409            return value
 410        return super().to_python(value)
 411
 412    def strptime(self, value, format):
 413        return datetime.datetime.strptime(value, format).time()
 414
 415
 416class DateTimeFormatsIterator:
 417    def __iter__(self):
 418        yield from BaseTemporalField.DATETIME_INPUT_FORMATS
 419        yield from BaseTemporalField.DATE_INPUT_FORMATS
 420
 421
 422class DateTimeField(BaseTemporalField):
 423    input_formats = DateTimeFormatsIterator()
 424    default_error_messages = {
 425        "invalid": "Enter a valid date/time.",
 426    }
 427
 428    def prepare_value(self, value):
 429        if isinstance(value, datetime.datetime):
 430            value = to_current_timezone(value)
 431        return value
 432
 433    def to_python(self, value):
 434        """
 435        Validate that the input can be converted to a datetime. Return a
 436        Python datetime.datetime object.
 437        """
 438        if value in self.empty_values:
 439            return None
 440        if isinstance(value, datetime.datetime):
 441            return from_current_timezone(value)
 442        if isinstance(value, datetime.date):
 443            result = datetime.datetime(value.year, value.month, value.day)
 444            return from_current_timezone(result)
 445        try:
 446            result = parse_datetime(value.strip())
 447        except ValueError:
 448            raise ValidationError(self.error_messages["invalid"], code="invalid")
 449        if not result:
 450            result = super().to_python(value)
 451        return from_current_timezone(result)
 452
 453    def strptime(self, value, format):
 454        return datetime.datetime.strptime(value, format)
 455
 456
 457class DurationField(Field):
 458    default_error_messages = {
 459        "invalid": "Enter a valid duration.",
 460        "overflow": "The number of days must be between {min_days} and {max_days}.",
 461    }
 462
 463    def prepare_value(self, value):
 464        if isinstance(value, datetime.timedelta):
 465            return duration_string(value)
 466        return value
 467
 468    def to_python(self, value):
 469        if value in self.empty_values:
 470            return None
 471        if isinstance(value, datetime.timedelta):
 472            return value
 473        try:
 474            value = parse_duration(str(value))
 475        except OverflowError:
 476            raise ValidationError(
 477                self.error_messages["overflow"].format(
 478                    min_days=datetime.timedelta.min.days,
 479                    max_days=datetime.timedelta.max.days,
 480                ),
 481                code="overflow",
 482            )
 483        if value is None:
 484            raise ValidationError(self.error_messages["invalid"], code="invalid")
 485        return value
 486
 487
 488class RegexField(CharField):
 489    def __init__(self, regex, **kwargs):
 490        """
 491        regex can be either a string or a compiled regular expression object.
 492        """
 493        kwargs.setdefault("strip", False)
 494        super().__init__(**kwargs)
 495        self._set_regex(regex)
 496
 497    def _get_regex(self):
 498        return self._regex
 499
 500    def _set_regex(self, regex):
 501        if isinstance(regex, str):
 502            regex = re.compile(regex)
 503        self._regex = regex
 504        if (
 505            hasattr(self, "_regex_validator")
 506            and self._regex_validator in self.validators
 507        ):
 508            self.validators.remove(self._regex_validator)
 509        self._regex_validator = validators.RegexValidator(regex=regex)
 510        self.validators.append(self._regex_validator)
 511
 512    regex = property(_get_regex, _set_regex)
 513
 514
 515class EmailField(CharField):
 516    default_validators = [validators.validate_email]
 517
 518    def __init__(self, **kwargs):
 519        super().__init__(strip=True, **kwargs)
 520
 521
 522class FileField(Field):
 523    default_error_messages = {
 524        "invalid": "No file was submitted. Check the encoding type on the form.",
 525        "missing": "No file was submitted.",
 526        "empty": "The submitted file is empty.",
 527        "text": pluralize_lazy(
 528            "Ensure this filename has at most %(max)d character (it has %(length)d).",
 529            "Ensure this filename has at most %(max)d characters (it has %(length)d).",
 530            "max",
 531        ),
 532        "contradiction": "Please either submit a file or check the clear checkbox, not both.",
 533    }
 534
 535    def __init__(self, *, max_length=None, allow_empty_file=False, **kwargs):
 536        self.max_length = max_length
 537        self.allow_empty_file = allow_empty_file
 538        super().__init__(**kwargs)
 539
 540    def to_python(self, data):
 541        if data in self.empty_values:
 542            return None
 543
 544        # UploadedFile objects should have name and size attributes.
 545        try:
 546            file_name = data.name
 547            file_size = data.size
 548        except AttributeError:
 549            raise ValidationError(self.error_messages["invalid"], code="invalid")
 550
 551        if self.max_length is not None and len(file_name) > self.max_length:
 552            params = {"max": self.max_length, "length": len(file_name)}
 553            raise ValidationError(
 554                self.error_messages["max_length"], code="max_length", params=params
 555            )
 556        if not file_name:
 557            raise ValidationError(self.error_messages["invalid"], code="invalid")
 558        if not self.allow_empty_file and not file_size:
 559            raise ValidationError(self.error_messages["empty"], code="empty")
 560
 561        return data
 562
 563    def clean(self, data, initial=None):
 564        # If the widget got contradictory inputs, we raise a validation error
 565        if data is FILE_INPUT_CONTRADICTION:
 566            raise ValidationError(
 567                self.error_messages["contradiction"], code="contradiction"
 568            )
 569        # False means the field value should be cleared; further validation is
 570        # not needed.
 571        if data is False:
 572            if not self.required:
 573                return False
 574            # If the field is required, clearing is not possible (the widget
 575            # shouldn't return False data in that case anyway). False is not
 576            # in self.empty_value; if a False value makes it this far
 577            # it should be validated from here on out as None (so it will be
 578            # caught by the required check).
 579            data = None
 580        if not data and initial:
 581            return initial
 582        return super().clean(data)
 583
 584    def bound_data(self, _, initial):
 585        return initial
 586
 587    def has_changed(self, initial, data):
 588        return data is not None
 589
 590    def value_from_form_data(self, data, files, html_name):
 591        return files.get(html_name)
 592
 593    def value_from_json_data(self, data, files, html_name):
 594        return files.get(html_name)
 595
 596
 597class ImageField(FileField):
 598    default_validators = [validators.validate_image_file_extension]
 599    default_error_messages = {
 600        "invalid_image": "Upload a valid image. The file you uploaded was either not an image or a corrupted image.",
 601    }
 602
 603    def to_python(self, data):
 604        """
 605        Check that the file-upload field data contains a valid image (GIF, JPG,
 606        PNG, etc. -- whatever Pillow supports).
 607        """
 608        f = super().to_python(data)
 609        if f is None:
 610            return None
 611
 612        from PIL import Image
 613
 614        # We need to get a file object for Pillow. We might have a path or we might
 615        # have to read the data into memory.
 616        if hasattr(data, "temporary_file_path"):
 617            file = data.temporary_file_path()
 618        else:
 619            if hasattr(data, "read"):
 620                file = BytesIO(data.read())
 621            else:
 622                file = BytesIO(data["content"])
 623
 624        try:
 625            # load() could spot a truncated JPEG, but it loads the entire
 626            # image in memory, which is a DoS vector. See #3848 and #18520.
 627            image = Image.open(file)
 628            # verify() must be called immediately after the constructor.
 629            image.verify()
 630
 631            # Annotating so subclasses can reuse it for their own validation
 632            f.image = image
 633            # Pillow doesn't detect the MIME type of all formats. In those
 634            # cases, content_type will be None.
 635            f.content_type = Image.MIME.get(image.format)
 636        except Exception as exc:
 637            # Pillow doesn't recognize it as an image.
 638            raise ValidationError(
 639                self.error_messages["invalid_image"],
 640                code="invalid_image",
 641            ) from exc
 642        if hasattr(f, "seek") and callable(f.seek):
 643            f.seek(0)
 644        return f
 645
 646
 647class URLField(CharField):
 648    default_error_messages = {
 649        "invalid": "Enter a valid URL.",
 650    }
 651    default_validators = [validators.URLValidator()]
 652
 653    def __init__(self, **kwargs):
 654        super().__init__(strip=True, **kwargs)
 655
 656    def to_python(self, value):
 657        def split_url(url):
 658            """
 659            Return a list of url parts via urlparse.urlsplit(), or raise
 660            ValidationError for some malformed URLs.
 661            """
 662            try:
 663                return list(urlsplit(url))
 664            except ValueError:
 665                # urlparse.urlsplit can raise a ValueError with some
 666                # misformatted URLs.
 667                raise ValidationError(self.error_messages["invalid"], code="invalid")
 668
 669        value = super().to_python(value)
 670        if value:
 671            url_fields = split_url(value)
 672            if not url_fields[0]:
 673                # If no URL scheme given, assume http://
 674                url_fields[0] = "http"
 675            if not url_fields[1]:
 676                # Assume that if no domain is provided, that the path segment
 677                # contains the domain.
 678                url_fields[1] = url_fields[2]
 679                url_fields[2] = ""
 680                # Rebuild the url_fields list, since the domain segment may now
 681                # contain the path too.
 682                url_fields = split_url(urlunsplit(url_fields))
 683            value = urlunsplit(url_fields)
 684        return value
 685
 686
 687class BooleanField(Field):
 688    def to_python(self, value):
 689        """Return a Python boolean object."""
 690        # Explicitly check for the string 'False', which is what a hidden field
 691        # will submit for False. Also check for '0', since this is what
 692        # RadioSelect will provide. Because bool("True") == bool('1') == True,
 693        # we don't need to handle that explicitly.
 694        if isinstance(value, str) and value.lower() in ("false", "0"):
 695            value = False
 696        else:
 697            value = bool(value)
 698        return super().to_python(value)
 699
 700    def validate(self, value):
 701        if not value and self.required:
 702            raise ValidationError(self.error_messages["required"], code="required")
 703
 704    def has_changed(self, initial, data):
 705        # Sometimes data or initial may be a string equivalent of a boolean
 706        # so we should run it through to_python first to get a boolean value
 707        return self.to_python(initial) != self.to_python(data)
 708
 709    def value_from_form_data(self, data, files, html_name):
 710        if html_name not in data:
 711            # Unselected checkboxes aren't in HTML form data, so return False
 712            return False
 713
 714        value = data.get(html_name)
 715        # Translate true and false strings to boolean values.
 716        return {
 717            True: True,
 718            "True": True,
 719            "False": False,
 720            False: False,
 721            "true": True,
 722            "false": False,
 723            "on": True,
 724        }.get(value)
 725
 726    def value_from_json_data(self, data, files, html_name):
 727        # Boolean fields must be present in the JSON data
 728        try:
 729            return data[html_name]
 730        except KeyError as e:
 731            raise FormFieldMissingError(html_name) from e
 732
 733
 734class NullBooleanField(BooleanField):
 735    """
 736    A field whose valid values are None, True, and False. Clean invalid values
 737    to None.
 738    """
 739
 740    def to_python(self, value):
 741        """
 742        Explicitly check for the string 'True' and 'False', which is what a
 743        hidden field will submit for True and False, for 'true' and 'false',
 744        which are likely to be returned by JavaScript serializations of forms,
 745        and for '1' and '0', which is what a RadioField will submit. Unlike
 746        the Booleanfield, this field must check for True because it doesn't
 747        use the bool() function.
 748        """
 749        if value in (True, "True", "true", "1"):
 750            return True
 751        elif value in (False, "False", "false", "0"):
 752            return False
 753        else:
 754            return None
 755
 756    def validate(self, value):
 757        pass
 758
 759
 760class CallableChoiceIterator:
 761    def __init__(self, choices_func):
 762        self.choices_func = choices_func
 763
 764    def __iter__(self):
 765        yield from self.choices_func()
 766
 767
 768class ChoiceField(Field):
 769    default_error_messages = {
 770        "invalid_choice": "Select a valid choice. %(value)s is not one of the available choices.",
 771    }
 772
 773    def __init__(self, *, choices=(), **kwargs):
 774        super().__init__(**kwargs)
 775        if hasattr(choices, "choices"):
 776            choices = choices.choices
 777        elif isinstance(choices, enum.EnumMeta):
 778            choices = [(member.value, member.name) for member in choices]
 779        self.choices = choices
 780
 781    def __deepcopy__(self, memo):
 782        result = super().__deepcopy__(memo)
 783        result._choices = copy.deepcopy(self._choices, memo)
 784        return result
 785
 786    def _get_choices(self):
 787        return self._choices
 788
 789    def _set_choices(self, value):
 790        # Setting choices also sets the choices on the widget.
 791        # choices can be any iterable, but we call list() on it because
 792        # it will be consumed more than once.
 793        if callable(value):
 794            value = CallableChoiceIterator(value)
 795        else:
 796            value = list(value)
 797
 798        self._choices = value
 799
 800    choices = property(_get_choices, _set_choices)
 801
 802    def to_python(self, value):
 803        """Return a string."""
 804        if value in self.empty_values:
 805            return ""
 806        return str(value)
 807
 808    def validate(self, value):
 809        """Validate that the input is in self.choices."""
 810        super().validate(value)
 811        if value and not self.valid_value(value):
 812            raise ValidationError(
 813                self.error_messages["invalid_choice"],
 814                code="invalid_choice",
 815                params={"value": value},
 816            )
 817
 818    def valid_value(self, value):
 819        """Check to see if the provided value is a valid choice."""
 820        text_value = str(value)
 821        for k, v in self.choices:
 822            if isinstance(v, list | tuple):
 823                # This is an optgroup, so look inside the group for options
 824                for k2, _ in v:
 825                    if value == k2 or text_value == str(k2):
 826                        return True
 827            else:
 828                if value == k or text_value == str(k):
 829                    return True
 830        return False
 831
 832
 833class TypedChoiceField(ChoiceField):
 834    def __init__(self, *, coerce=lambda val: val, empty_value="", **kwargs):
 835        self.coerce = coerce
 836        self.empty_value = empty_value
 837        super().__init__(**kwargs)
 838
 839    def _coerce(self, value):
 840        """
 841        Validate that the value can be coerced to the right type (if not empty).
 842        """
 843        if value == self.empty_value or value in self.empty_values:
 844            return self.empty_value
 845        try:
 846            value = self.coerce(value)
 847        except (ValueError, TypeError, ValidationError):
 848            raise ValidationError(
 849                self.error_messages["invalid_choice"],
 850                code="invalid_choice",
 851                params={"value": value},
 852            )
 853        return value
 854
 855    def clean(self, value):
 856        value = super().clean(value)
 857        return self._coerce(value)
 858
 859
 860class MultipleChoiceField(ChoiceField):
 861    default_error_messages = {
 862        "invalid_choice": "Select a valid choice. %(value)s is not one of the available choices.",
 863        "invalid_list": "Enter a list of values.",
 864    }
 865
 866    def to_python(self, value):
 867        if not value:
 868            return []
 869        elif not isinstance(value, list | tuple):
 870            raise ValidationError(
 871                self.error_messages["invalid_list"], code="invalid_list"
 872            )
 873        return [str(val) for val in value]
 874
 875    def validate(self, value):
 876        """Validate that the input is a list or tuple."""
 877        if self.required and not value:
 878            raise ValidationError(self.error_messages["required"], code="required")
 879        # Validate that each value in the value list is in self.choices.
 880        for val in value:
 881            if not self.valid_value(val):
 882                raise ValidationError(
 883                    self.error_messages["invalid_choice"],
 884                    code="invalid_choice",
 885                    params={"value": val},
 886                )
 887
 888    def has_changed(self, initial, data):
 889        if initial is None:
 890            initial = []
 891        if data is None:
 892            data = []
 893        if len(initial) != len(data):
 894            return True
 895        initial_set = {str(value) for value in initial}
 896        data_set = {str(value) for value in data}
 897        return data_set != initial_set
 898
 899    def value_from_form_data(self, data, files, html_name):
 900        return data.getlist(html_name)
 901
 902
 903class UUIDField(CharField):
 904    default_error_messages = {
 905        "invalid": "Enter a valid UUID.",
 906    }
 907
 908    def prepare_value(self, value):
 909        if isinstance(value, uuid.UUID):
 910            return str(value)
 911        return value
 912
 913    def to_python(self, value):
 914        value = super().to_python(value)
 915        if value in self.empty_values:
 916            return None
 917        if not isinstance(value, uuid.UUID):
 918            try:
 919                value = uuid.UUID(value)
 920            except ValueError:
 921                raise ValidationError(self.error_messages["invalid"], code="invalid")
 922        return value
 923
 924
 925class InvalidJSONInput(str):
 926    pass
 927
 928
 929class JSONString(str):
 930    pass
 931
 932
 933class JSONField(CharField):
 934    default_error_messages = {
 935        "invalid": "Enter a valid JSON.",
 936    }
 937
 938    def __init__(
 939        self, encoder=None, decoder=None, indent=None, sort_keys=False, **kwargs
 940    ):
 941        self.encoder = encoder
 942        self.decoder = decoder
 943        self.indent = indent
 944        self.sort_keys = sort_keys
 945        super().__init__(**kwargs)
 946
 947    def to_python(self, value):
 948        if value in self.empty_values:
 949            return None
 950        elif isinstance(value, list | dict | int | float | JSONString):
 951            return value
 952        try:
 953            converted = json.loads(value, cls=self.decoder)
 954        except json.JSONDecodeError:
 955            raise ValidationError(
 956                self.error_messages["invalid"],
 957                code="invalid",
 958                params={"value": value},
 959            )
 960        if isinstance(converted, str):
 961            return JSONString(converted)
 962        else:
 963            return converted
 964
 965    def bound_data(self, data, initial):
 966        if data is None:
 967            return None
 968        try:
 969            return json.loads(data, cls=self.decoder)
 970        except json.JSONDecodeError:
 971            return InvalidJSONInput(data)
 972
 973    def prepare_value(self, value):
 974        if isinstance(value, InvalidJSONInput):
 975            return value
 976        return json.dumps(
 977            value,
 978            indent=self.indent,
 979            sort_keys=self.sort_keys,
 980            ensure_ascii=False,
 981            cls=self.encoder,
 982        )
 983
 984    def has_changed(self, initial, data):
 985        if super().has_changed(initial, data):
 986            return True
 987        # For purposes of seeing whether something has changed, True isn't the
 988        # same as 1 and the order of keys doesn't matter.
 989        return json.dumps(initial, sort_keys=True, cls=self.encoder) != json.dumps(
 990            self.to_python(data), sort_keys=True, cls=self.encoder
 991        )
 992
 993
 994def from_current_timezone(value):
 995    """
 996    When time zone support is enabled, convert naive datetimes
 997    entered in the current time zone to aware datetimes.
 998    """
 999    if value is not None and timezone.is_naive(value):
1000        current_timezone = timezone.get_current_timezone()
1001        try:
1002            if timezone._datetime_ambiguous_or_imaginary(value, current_timezone):
1003                raise ValueError("Ambiguous or non-existent time.")
1004            return timezone.make_aware(value, current_timezone)
1005        except Exception as exc:
1006            raise ValidationError(
1007                (
1008                    "%(datetime)s couldn’t be interpreted "
1009                    "in time zone %(current_timezone)s; it "
1010                    "may be ambiguous or it may not exist."
1011                ),
1012                code="ambiguous_timezone",
1013                params={"datetime": value, "current_timezone": current_timezone},
1014            ) from exc
1015    return value
1016
1017
1018def to_current_timezone(value):
1019    """
1020    When time zone support is enabled, convert aware datetimes
1021    to naive datetimes in the current time zone for display.
1022    """
1023    if value is not None and timezone.is_aware(value):
1024        return timezone.make_naive(value)
1025    return value