field classes add an invalid entry to defaulterrormessage if you want a specific field error message not raised by the field validators required boolean that specifies whether the field is required true by default widget a widget class or instance of a widget class that should be used for this field when displaying it each field has a default widget that it ll use if you don t specify this in most cases the default widget is textinput label a verbose name for this field for use in displaying this field in a form by default django will use a pretty version of the form field name if the field is part of a form initial a value to use in this field s initial display this value is not used as a fallback if data isn t given helptext an optional string to use as help text for this field errormessages an optional dictionary to override the default messages that the field will raise showhiddeninitial boolean that specifies if it is needed to render a hidden widget with initial value after widget validators list of additional validators to use localize boolean that specifies if the field should be localized disabled boolean that specifies whether the field is disabled that is its widget is shown in the form but not editable labelsuffix suffix to be added to the label overrides form s labelsuffix trigger the localization machinery if needed let the widget know whether it should display as required hook into self widgetattrs for any fieldspecific html attributes validate the given value and return its cleaned value as an appropriate python object raise validationerror for any errors return the value that should be shown for this field on render of a bound form given the submitted post data for the field and the initial data if any for most fields this will simply be data filefields need to handle it a bit differently given a widget instance not a widget class return a dictionary of any html attributes that should be added to the widget based on this field return true if data differs from initial always return false if the field is disabled since self bounddata always uses the initial value in this case if self disabled return false try data self topythondata if hasattrself coerce return self coercedata self coerceinitial 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 initialvalue initial if initial is not none else datavalue data if data is not none else return initialvalue datavalue def getboundfieldself form fieldname return boundfieldform self fieldname def deepcopyself memo result copy copyself memoidself result result widget copy deepcopyself widget memo result errormessages self errormessages copy result validators self validators return result class charfieldfield def init self maxlengthnone minlengthnone striptrue emptyvalue kwargs self maxlength maxlength self minlength minlength self strip strip self emptyvalue emptyvalue super initkwargs if minlength is not none self validators appendvalidators minlengthvalidatorintminlength if maxlength is not none self validators appendvalidators maxlengthvalidatorintmaxlength self validators appendvalidators prohibitnullcharactersvalidator def topythonself value the html attribute is maxlength not maxlength the html attribute is minlength not minlength localized number input is not well supported on most browsers validate that int can be called on the input return the result of int or none for empty values strip trailing decimal and zeros validate that float can be called on the input return the result of float or none for empty values validate that the input is a decimal number return a decimal instance or none for empty values ensure that there are no more than maxdigits in the number and no more than decimalplaces digits after the decimal point use exponential notation for small values since they might be parsed as 0 otherwise ref 20765 try to strptime against each input format validate that the input can be converted to a date return a python datetime date object validate that the input can be converted to a time return a python datetime time object validate that the input can be converted to a datetime return a python datetime datetime object regex can be either a string or a compiled regular expression object the default maximum length of an email is 320 characters per rfc 3696 section 3 uploadedfile objects should have name and size attributes if the widget got contradictory inputs we raise a validation error false means the field value should be cleared further validation is not needed if the field is required clearing is not possible the widget shouldn t return false data in that case anyway false is not in self emptyvalue if a false value makes it this far it should be validated from here on out as none so it will be caught by the required check check that the fileupload field data contains a valid image gif jpg png etc whatever pillow supports we need to get a file object for pillow we might have a path or we might have to read the data into memory load could spot a truncated jpeg but it loads the entire image in memory which is a dos vector see 3848 and 18520 verify must be called immediately after the constructor annotating so subclasses can reuse it for their own validation pillow doesn t detect the mime type of all formats in those cases contenttype will be none pillow doesn t recognize it as an image removedindjango60warning when the deprecation ends replace with self assumescheme assumescheme or https return a list of url parts via urlparse urlsplit or raise validationerror for some malformed urls urlparse urlsplit can raise a valueerror with some misformatted urls if no url scheme given add a scheme assume that if no domain is provided that the path segment contains the domain rebuild the urlfields list since the domain segment may now contain the path too return a python boolean object explicitly check for the string false which is what a hidden field will submit for false also check for 0 since this is what radioselect will provide because booltrue bool 1 true we don t need to handle that explicitly if isinstancevalue str and value lower in false 0 value false else value boolvalue return super topythonvalue def validateself value if not value and self required raise validationerrorself errormessagesrequired coderequired def haschangedself initial data if self disabled return false sometimes data or initial may be a string equivalent of a boolean so we should run it through topython first to get a boolean value return self topythoninitial self topythondata class nullbooleanfieldbooleanfield widget nullbooleanselect def topythonself value if value in true true true 1 return true elif value in false false false 0 return false else return none def validateself value pass class choicefieldfield widget select defaulterrormessages invalidchoice select a valid choice values is not one of the available choices def initself choices kwargs super initkwargs self choices choices def deepcopyself memo result super deepcopymemo result choices copy deepcopyself choices memo return result property def choicesself return self choices choices setter def choicesself value setting choices on the field also sets the choices on the widget note that the property setter for the widget will renormalize self choices self widget choices normalizechoicesvalue def topythonself value validate that the input is in self choices super validatevalue if value and not self validvaluevalue raise validationerror self errormessagesinvalidchoice codeinvalidchoice paramsvalue value def validvalueself value this is an optgroup so look inside the group for options validate that the value can be coerced to the right type if not empty validate that the input is a list or tuple if self required and not value raise validationerrorself errormessagesrequired coderequired validate that each value in the value list is in self choices for val in value if not self validvalueval raise validationerror self errormessagesinvalidchoice codeinvalidchoice paramsvalue val def haschangedself initial data if self disabled return false if initial is none initial if data is none data if leninitial lendata return true initialset strvalue for value in initial dataset strvalue for value in data return dataset initialset class typedmultiplechoicefieldmultiplechoicefield def initself coercelambda val val kwargs self coerce coerce self emptyvalue kwargs popemptyvalue super initkwargs def coerceself value if value self emptyvalue or value in self emptyvalues return self emptyvalue newvalue for choice in value try newvalue appendself coercechoice except valueerror typeerror validationerror raise validationerror self errormessagesinvalidchoice codeinvalidchoice paramsvalue choice return newvalue def cleanself value value super cleanvalue return self coercevalue def validateself value if value self emptyvalue super validatevalue elif self required raise validationerrorself errormessagesrequired coderequired class combofieldfield def initself fields kwargs super initkwargs set required to false on the individual fields because the required validation will be handled by combofield not by those individual fields for f in fields f required false self fields fields def cleanself value super cleanvalue for field in self fields value field cleanvalue return value class multivaluefieldfield defaulterrormessages invalid enter a list of values incomplete enter a complete value def initself fields requireallfieldstrue kwargs self requireallfields requireallfields super initkwargs for f in fields f errormessages setdefaultincomplete self errormessagesincomplete if self disabled f disabled true if self requireallfields set required to false on the individual fields because the required validation will be handled by multivaluefield not by those individual fields f required false self fields fields def deepcopyself memo result super deepcopymemo result fields tuplex deepcopymemo for x in self fields return result def validateself value pass def cleanself value cleandata errors if self disabled and not isinstancevalue list value self widget decompressvalue if not value or isinstancevalue list tuple if not value or not v for v in value if v not in self emptyvalues if self required raise validationerror self errormessagesrequired coderequired else return self compress else raise validationerrorself errormessagesinvalid codeinvalid for i field in enumerateself fields try fieldvalue valuei except indexerror fieldvalue none if fieldvalue in self emptyvalues if self requireallfields raise a required error if the multivaluefield is required and any field is empty if self required raise validationerror self errormessagesrequired coderequired 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 errormessagesincomplete not in errors errors appendfield errormessagesincomplete continue try cleandata appendfield cleanfieldvalue except validationerror as e collect all validation errors in a single list which we ll raise at the end of clean rather than raising a single exception for the first error we encounter skip duplicates errors extendm for m in e errorlist if m not in errors if errors raise validationerrorerrors out self compresscleandata self validateout self runvalidatorsout return out def compressself datalist raise notimplementederrorsubclasses must implement this method def haschangedself initial data if self disabled return false if initial is none initial for x in range0 lendata else if not isinstanceinitial list initial self widget decompressinitial for field initial data in zipself fields initial data try initial field topythoninitial except validationerror return true if field haschangedinitial data return true return false class filepathfieldchoicefield def init self path matchnone recursivefalse allowfilestrue allowfoldersfalse kwargs self path self match self recursive path match recursive self allowfiles self allowfolders allowfiles allowfolders super initchoices kwargs if self required self choices else self choices if self match is not none self matchre re compileself match if recursive for root dirs files in sortedos walkself path if self allowfiles for f in sortedfiles if self match is none or self matchre searchf f os path joinroot f self choices appendf f replacepath 1 if self allowfolders for f in sorteddirs if f pycache continue if self match is none or self matchre searchf f os path joinroot f self choices appendf f replacepath 1 else choices with os scandirself path as entries for f in entries if f name pycache continue if self allowfiles and f isfile or self allowfolders and f isdir and self match is none or self matchre searchf name choices appendf path f name choices sortkeyoperator itemgetter1 self choices extendchoices self widget choices self choices class splitdatetimefieldmultivaluefield widget splitdatetimewidget hiddenwidget splithiddendatetimewidget defaulterrormessages invaliddate enter a valid date invalidtime enter a valid time def initself inputdateformatsnone inputtimeformatsnone kwargs errors self defaulterrormessages copy if errormessages in kwargs errors updatekwargserrormessages localize kwargs getlocalize false fields datefield inputformatsinputdateformats errormessagesinvalid errorsinvaliddate localizelocalize timefield inputformatsinputtimeformats errormessagesinvalid errorsinvalidtime localizelocalize super initfields kwargs def compressself datalist if datalist raise a validation error if time or date is empty possible if splitdatetimefield has requiredfalse if datalist0 in self emptyvalues raise validationerror self errormessagesinvaliddate codeinvaliddate if datalist1 in self emptyvalues raise validationerror self errormessagesinvalidtime codeinvalidtime result datetime datetime combinedatalist return fromcurrenttimezoneresult return none class genericipaddressfieldcharfield def initself protocolboth unpackipv4false kwargs self unpackipv4 unpackipv4 self defaultvalidators validators ipaddressvalidators protocol unpackipv4 super initkwargs def topythonself value if value in self emptyvalues return value value strip if value and in value return cleanipv6addressvalue self unpackipv4 return value class slugfieldcharfield defaultvalidators validators validateslug def initself allowunicodefalse kwargs self allowunicode allowunicode if self allowunicode self defaultvalidators validators validateunicodeslug super initkwargs class uuidfieldcharfield defaulterrormessages invalid enter a valid uuid def preparevalueself value if isinstancevalue uuid uuid return strvalue return value def topythonself value value super topythonvalue if value in self emptyvalues return none if not isinstancevalue uuid uuid try value uuid uuidvalue except valueerror raise validationerrorself errormessagesinvalid codeinvalid return value class invalidjsoninputstr pass class jsonstringstr pass class jsonfieldcharfield defaulterrormessages invalid enter a valid json widget textarea def initself encodernone decodernone kwargs self encoder encoder self decoder decoder super initkwargs def topythonself value if self disabled return value if value in self emptyvalues return none elif isinstancevalue list dict int float jsonstring return value try converted json loadsvalue clsself decoder except json jsondecodeerror raise validationerror self errormessagesinvalid codeinvalid paramsvalue value if isinstanceconverted str return jsonstringconverted else return converted def bounddataself data initial if self disabled return initial if data is none return none try return json loadsdata clsself decoder except json jsondecodeerror return invalidjsoninputdata def preparevalueself value if isinstancevalue invalidjsoninput return value return json dumpsvalue ensureasciifalse clsself encoder def haschangedself initial data if super haschangedinitial data 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 dumpsinitial sortkeystrue clsself encoder json dumps self topythondata sortkeystrue clsself encoder default widget to use when rendering this type of field default widget to use when rendering this as hidden 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 required boolean that specifies whether the field is required true by default widget a widget class or instance of a widget class that should be used for this field when displaying it each field has a default widget that it ll use if you don t specify this in most cases the default widget is textinput label a verbose name for this field for use in displaying this field in a form by default django will use a pretty version of the form field name if the field is part of a form initial a value to use in this field s initial display this value is not used as a fallback if data isn t given help_text an optional string to use as help text for this field error_messages an optional dictionary to override the default messages that the field will raise show_hidden_initial boolean that specifies if it is needed to render a hidden widget with initial value after widget validators list of additional validators to use localize boolean that specifies if the field should be localized disabled boolean that specifies whether the field is disabled that is its widget is shown in the form but not editable label_suffix suffix to be added to the label overrides form s label_suffix trigger the localization machinery if needed let the widget know whether it should display as required hook into self widget_attrs for any field specific html attributes validate the given value and return its cleaned value as an appropriate python object raise validationerror for any errors return the value that should be shown for this field on render of a bound form given the submitted post data for the field and the initial data if any for most fields this will simply be data filefields need to handle it a bit differently given a widget instance not a widget class return a dictionary of any html attributes that should be added to the widget based on this field return true if data differs from initial always return false if the field is disabled since self bound_data always uses the initial value in this case 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 return a boundfield instance that will be used when accessing the form field in a template return a string the html attribute is maxlength not max_length the html attribute is minlength not min_length localized number input is not well supported on most browsers validate that int can be called on the input return the result of int or none for empty values strip trailing decimal and zeros validate that float can be called on the input return the result of float or none for empty values validate that the input is a decimal number return a decimal instance or none for empty values ensure that there are no more than max_digits in the number and no more than decimal_places digits after the decimal point use exponential notation for small values since they might be parsed as 0 otherwise ref 20765 try to strptime against each input format validate that the input can be converted to a date return a python datetime date object validate that the input can be converted to a time return a python datetime time object validate that the input can be converted to a datetime return a python datetime datetime object regex can be either a string or a compiled regular expression object the default maximum length of an email is 320 characters per rfc 3696 section 3 uploadedfile objects should have name and size attributes if the widget got contradictory inputs we raise a validation error false means the field value should be cleared further validation is not needed if the field is required clearing is not possible the widget shouldn t return false data in that case anyway false is not in self empty_value if a false value makes it this far it should be validated from here on out as none so it will be caught by the required check check that the file upload field data contains a valid image gif jpg png etc whatever pillow supports we need to get a file object for pillow we might have a path or we might have to read the data into memory load could spot a truncated jpeg but it loads the entire image in memory which is a dos vector see 3848 and 18520 verify must be called immediately after the constructor annotating so subclasses can reuse it for their own validation pillow doesn t detect the mime type of all formats in those cases content_type will be none pillow doesn t recognize it as an image removedindjango60warning when the deprecation ends replace with self assume_scheme assume_scheme or https return a list of url parts via urlparse urlsplit or raise validationerror for some malformed urls urlparse urlsplit can raise a valueerror with some misformatted urls if no url scheme given add a scheme assume that if no domain is provided that the path segment contains the domain rebuild the url_fields list since the domain segment may now contain the path too return a python boolean object explicitly check for the string false which is what a hidden 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 sometimes data or initial may be a string equivalent of a boolean so we should run it through to_python first to get a boolean value a field whose valid values are none true and false clean invalid values to none explicitly check for the string true and false which is what a hidden field will submit for true and false for true and false which are likely to be returned by javascript serializations of forms and for 1 and 0 which is what a radiofield will submit unlike the booleanfield this field must check for true because it doesn t use the bool function setting choices on the field also sets the choices on the widget note that the property setter for the widget will re normalize return a string validate that the input is in self choices check to see if the provided value is a valid choice this is an optgroup so look inside the group for options validate that the value can be coerced to the right type if not empty validate that the input is a list or tuple validate that each value in the value list is in self choices validate that the values are in self choices and can be coerced to the right type a field whose clean method calls multiple field clean methods set required to false on the individual fields because the required validation will be handled by combofield not by those individual fields validate the given value against all of self fields which is a list of field instances aggregate the logic of multiple fields its clean method takes a decompressed list of values which are then cleaned into a single value according to self fields each value in this list is cleaned by the corresponding field the first value is cleaned by the first field the second value is cleaned by the second field etc once all fields are cleaned the list of clean values is compressed into a single value subclasses should not have to implement clean instead they must implement compress which takes a list of valid values and returns a compressed version of those values a single value you ll probably want to use this with multiwidget set required to false on the individual fields because the required validation will be handled by multivaluefield not by those individual fields validate every value in the given list a value is validated against the corresponding field in self fields for example if this multivaluefield was instantiated with fields datefield timefield clean would call datefield clean value 0 and timefield clean value 1 raise a required error if the multivaluefield is required and any field is empty otherwise add an incomplete error to the list of collected errors and skip field cleaning if a required field is empty collect all validation errors in a single list which we ll raise at the end of clean rather than raising a single exception for the first error we encounter skip duplicates return a single value for the given list of values the values can be assumed to be valid for example if this multivaluefield was instantiated with fields datefield timefield this might return a datetime object combining the date and time in data_list raise a validation error if time or date is empty possible if splitdatetimefield has required false for purposes of seeing whether something has changed true isn t the same as 1 and the order of keys doesn t matter
import copy
import datetime
import json
import math
import operator
import os
import re
import uuid
import warnings
from decimal import Decimal, DecimalException
from io import BytesIO
from urllib.parse import urlsplit, urlunsplit

from django.conf import settings
from django.core import validators
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,
)
from django.utils import formats
from django.utils.choices import normalize_choices
from django.utils.dateparse import parse_datetime, parse_duration
from django.utils.deprecation import RemovedInDjango60Warning
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 _
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",
)


class Field:
    widget = TextInput  
    hidden_widget = (
        HiddenInput  
    )
    default_validators = []  
    
    
    default_error_messages = {
        "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,
        template_name=None,
    ):
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        self.required, self.label, self.initial = required, label, initial
        self.show_hidden_initial = show_hidden_initial
        self.help_text = help_text
        self.disabled = disabled
        self.label_suffix = label_suffix
        widget = widget or self.widget
        if isinstance(widget, type):
            widget = widget()
        else:
            widget = copy.deepcopy(widget)

        
        self.localize = localize
        if self.localize:
            widget.is_localized = True

        
        widget.is_required = self.required

        
        extra_attrs = self.widget_attrs(widget)
        if extra_attrs:
            widget.attrs.update(extra_attrs)

        self.widget = widget

        messages = {}
        for c in reversed(self.__class__.__mro__):
            messages.update(getattr(c, "default_error_messages", {}))
        messages.update(error_messages or {})
        self.error_messages = messages

        self.validators = [*self.default_validators, *validators]
        self.template_name = template_name

        super().__init__()

    def prepare_value(self, value):
        return value

    def to_python(self, value):
        return value

    def validate(self, value):
        if value in self.empty_values and self.required:
            raise ValidationError(self.error_messages["required"], code="required")

    def run_validators(self, value):
        if value in self.empty_values:
            return
        errors = []
        for v in self.validators:
            try:
                v(value)
            except ValidationError as e:
                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:
            raise ValidationError(errors)

    def clean(self, value):
        
        value = self.to_python(value)
        self.validate(value)
        self.run_validators(value)
        return value

    def bound_data(self, data, initial):
        
        if self.disabled:
            return initial
        return data

    def widget_attrs(self, widget):
        
        return {}

    def has_changed(self, initial, data):
        
        
        
        if self.disabled:
            return False
        try:
            data = self.to_python(data)
            if hasattr(self, "_coerce"):
                return self._coerce(data) != self._coerce(initial)
        except ValidationError:
            return True
        
        
        
        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):
        
        return BoundField(form, self, field_name)

    def __deepcopy__(self, memo):
        result = copy.copy(self)
        memo[id(self)] = result
        result.widget = copy.deepcopy(self.widget, memo)
        result.error_messages = self.error_messages.copy()
        result.validators = self.validators[:]
        return result


class CharField(Field):
    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
        self.empty_value = empty_value
        super().__init__(**kwargs)
        if min_length is not None:
            self.validators.append(validators.MinLengthValidator(int(min_length)))
        if max_length is not None:
            self.validators.append(validators.MaxLengthValidator(int(max_length)))
        self.validators.append(validators.ProhibitNullCharactersValidator())

    def to_python(self, value):
        
        if value not in self.empty_values:
            value = str(value)
            if self.strip:
                value = value.strip()
        if value in self.empty_values:
            return self.empty_value
        return value

    def widget_attrs(self, widget):
        attrs = super().widget_attrs(widget)
        if self.max_length is not None and not widget.is_hidden:
            
            attrs["maxlength"] = str(self.max_length)
        if self.min_length is not None and not widget.is_hidden:
            
            attrs["minlength"] = str(self.min_length)
        return attrs


class IntegerField(Field):
    widget = NumberInput
    default_error_messages = {
        "invalid": _("Enter a whole number."),
    }
    re_decimal = _lazy_re_compile(r"\.0*\s*$")

    def __init__(self, *, max_value=None, min_value=None, step_size=None, **kwargs):
        self.max_value, self.min_value, self.step_size = max_value, min_value, step_size
        if kwargs.get("localize") and self.widget == NumberInput:
            
            kwargs.setdefault("widget", super().widget)
        super().__init__(**kwargs)

        if max_value is not None:
            self.validators.append(validators.MaxValueValidator(max_value))
        if min_value is not None:
            self.validators.append(validators.MinValueValidator(min_value))
        if step_size is not None:
            self.validators.append(
                validators.StepValueValidator(step_size, offset=min_value)
            )

    def to_python(self, value):
        
        value = super().to_python(value)
        if value in self.empty_values:
            return None
        if self.localize:
            value = formats.sanitize_separators(value)
        
        try:
            value = int(self.re_decimal.sub("", str(value)))
        except (ValueError, TypeError):
            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
            if self.max_value is not None:
                attrs["max"] = self.max_value
            if self.step_size is not None:
                attrs["step"] = self.step_size
        return attrs


class FloatField(IntegerField):
    default_error_messages = {
        "invalid": _("Enter a number."),
    }

    def to_python(self, value):
        
        value = super(IntegerField, self).to_python(value)
        if value in self.empty_values:
            return None
        if self.localize:
            value = formats.sanitize_separators(value)
        try:
            value = float(value)
        except (ValueError, TypeError):
            raise ValidationError(self.error_messages["invalid"], code="invalid")
        return value

    def validate(self, value):
        super().validate(value)
        if value in self.empty_values:
            return
        if not math.isfinite(value):
            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:
            if self.step_size is not None:
                step = str(self.step_size)
            else:
                step = "any"
            attrs.setdefault("step", step)
        return attrs


class DecimalField(IntegerField):
    default_error_messages = {
        "invalid": _("Enter a number."),
    }

    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))

    def to_python(self, value):
        
        if value in self.empty_values:
            return None
        if self.localize:
            value = formats.sanitize_separators(value)
        try:
            value = Decimal(str(value))
        except DecimalException:
            raise ValidationError(self.error_messages["invalid"], code="invalid")
        return value

    def validate(self, value):
        super().validate(value)
        if value in self.empty_values:
            return
        if not value.is_finite():
            raise ValidationError(
                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 self.decimal_places is not None:
                
                
                step = str(Decimal(1).scaleb(-self.decimal_places)).lower()
            else:
                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:
            self.input_formats = input_formats

    def to_python(self, value):
        value = value.strip()
        
        for format in self.input_formats:
            try:
                return self.strptime(value, format)
            except (ValueError, TypeError):
                continue
        raise ValidationError(self.error_messages["invalid"], code="invalid")

    def strptime(self, value, format):
        raise NotImplementedError("Subclasses must define this method.")


class DateField(BaseTemporalField):
    widget = DateInput
    input_formats = formats.get_format_lazy("DATE_INPUT_FORMATS")
    default_error_messages = {
        "invalid": _("Enter a valid date."),
    }

    def to_python(self, value):
        
        if value in self.empty_values:
            return None
        if isinstance(value, datetime.datetime):
            return value.date()
        if isinstance(value, datetime.date):
            return value
        return super().to_python(value)

    def strptime(self, value, format):
        return datetime.datetime.strptime(value, format).date()


class TimeField(BaseTemporalField):
    widget = TimeInput
    input_formats = formats.get_format_lazy("TIME_INPUT_FORMATS")
    default_error_messages = {"invalid": _("Enter a valid time.")}

    def to_python(self, value):
        
        if value in self.empty_values:
            return None
        if isinstance(value, datetime.time):
            return value
        return super().to_python(value)

    def strptime(self, value, format):
        return datetime.datetime.strptime(value, format).time()


class DateTimeFormatsIterator:
    def __iter__(self):
        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."),
    }

    def prepare_value(self, value):
        if isinstance(value, datetime.datetime):
            value = to_current_timezone(value)
        return value

    def to_python(self, value):
        
        if value in self.empty_values:
            return None
        if isinstance(value, datetime.datetime):
            return from_current_timezone(value)
        if isinstance(value, datetime.date):
            result = datetime.datetime(value.year, value.month, value.day)
            return from_current_timezone(result)
        try:
            result = parse_datetime(value.strip())
        except ValueError:
            raise ValidationError(self.error_messages["invalid"], code="invalid")
        if not result:
            result = super().to_python(value)
        return from_current_timezone(result)

    def strptime(self, value, format):
        return datetime.datetime.strptime(value, format)


class DurationField(Field):
    default_error_messages = {
        "invalid": _("Enter a valid duration."),
        "overflow": _("The number of days must be between {min_days} and {max_days}."),
    }

    def prepare_value(self, value):
        if isinstance(value, datetime.timedelta):
            return duration_string(value)
        return value

    def to_python(self, value):
        if value in self.empty_values:
            return None
        if isinstance(value, datetime.timedelta):
            return value
        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",
            )
        if value is None:
            raise ValidationError(self.error_messages["invalid"], code="invalid")
        return value


class RegexField(CharField):
    def __init__(self, regex, **kwargs):
        
        kwargs.setdefault("strip", False)
        super().__init__(**kwargs)
        self._set_regex(regex)

    def _get_regex(self):
        return self._regex

    def _set_regex(self, regex):
        if isinstance(regex, str):
            regex = re.compile(regex)
        self._regex = regex
        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)

    regex = property(_get_regex, _set_regex)


class EmailField(CharField):
    widget = EmailInput
    default_validators = [validators.validate_email]

    def __init__(self, **kwargs):
        
        
        kwargs.setdefault("max_length", 320)
        super().__init__(strip=True, **kwargs)


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."
        ),
    }

    def __init__(self, *, max_length=None, allow_empty_file=False, **kwargs):
        self.max_length = max_length
        self.allow_empty_file = allow_empty_file
        super().__init__(**kwargs)

    def to_python(self, data):
        if data in self.empty_values:
            return None

        
        try:
            file_name = data.name
            file_size = data.size
        except AttributeError:
            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
            )
        if not file_name:
            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")

        return data

    def clean(self, data, initial=None):
        
        if data is FILE_INPUT_CONTRADICTION:
            raise ValidationError(
                self.error_messages["contradiction"], code="contradiction"
            )
        
        
        if data is False:
            if not self.required:
                return False
            
            
            
            
            
            data = None
        if not data and initial:
            return initial
        return super().clean(data)

    def bound_data(self, _, initial):
        return initial

    def has_changed(self, initial, data):
        return not self.disabled and data is not None


class ImageField(FileField):
    default_validators = [validators.validate_image_file_extension]
    default_error_messages = {
        "invalid_image": _(
            "Upload a valid image. The file you uploaded was either not an "
            "image or a corrupted image."
        ),
    }

    def to_python(self, data):
        
        f = super().to_python(data)
        if f is None:
            return None

        from PIL import Image

        
        
        if hasattr(data, "temporary_file_path"):
            file = data.temporary_file_path()
        else:
            if hasattr(data, "read"):
                file = BytesIO(data.read())
            else:
                file = BytesIO(data["content"])

        try:
            
            
            image = Image.open(file)
            
            image.verify()

            
            f.image = image
            
            
            f.content_type = Image.MIME.get(image.format)
        except Exception as exc:
            
            raise ValidationError(
                self.error_messages["invalid_image"],
                code="invalid_image",
            ) from exc
        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/*")
        return attrs


class URLField(CharField):
    widget = URLInput
    default_error_messages = {
        "invalid": _("Enter a valid URL."),
    }
    default_validators = [validators.URLValidator()]

    def __init__(self, *, assume_scheme=None, **kwargs):
        if assume_scheme is None:
            if settings.FORMS_URLFIELD_ASSUME_HTTPS:
                assume_scheme = "https"
            else:
                warnings.warn(
                    "The default scheme will be changed from 'http' to 'https' in "
                    "Django 6.0. Pass the forms.URLField.assume_scheme argument to "
                    "silence this warning, or set the FORMS_URLFIELD_ASSUME_HTTPS "
                    "transitional setting to True to opt into using 'https' as the new "
                    "default scheme.",
                    RemovedInDjango60Warning,
                    stacklevel=2,
                )
                assume_scheme = "http"
        
        
        self.assume_scheme = assume_scheme
        super().__init__(strip=True, **kwargs)

    def to_python(self, value):
        def split_url(url):
            
            try:
                return list(urlsplit(url))
            except ValueError:
                
                
                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]:
                
                url_fields[0] = self.assume_scheme
            if not url_fields[1]:
                
                
                url_fields[1] = url_fields[2]
                url_fields[2] = ""
                
                
                url_fields = split_url(urlunsplit(url_fields))
            value = urlunsplit(url_fields)
        return value


class BooleanField(Field):
    widget = CheckboxInput

    def to_python(self, value):
        
        
        
        
        
        if isinstance(value, str) and value.lower() in ("false", "0"):
            value = False
        else:
            value = bool(value)
        return super().to_python(value)

    def validate(self, value):
        if not value and self.required:
            raise ValidationError(self.error_messages["required"], code="required")

    def has_changed(self, initial, data):
        if self.disabled:
            return False
        
        
        return self.to_python(initial) != self.to_python(data)


class NullBooleanField(BooleanField):
    

    widget = NullBooleanSelect

    def to_python(self, value):
        
        if value in (True, "True", "true", "1"):
            return True
        elif value in (False, "False", "false", "0"):
            return False
        else:
            return None

    def validate(self, value):
        pass


class ChoiceField(Field):
    widget = Select
    default_error_messages = {
        "invalid_choice": _(
            "Select a valid choice. %(value)s is not one of the available choices."
        ),
    }

    def __init__(self, *, choices=(), **kwargs):
        super().__init__(**kwargs)
        self.choices = choices

    def __deepcopy__(self, memo):
        result = super().__deepcopy__(memo)
        result._choices = copy.deepcopy(self._choices, memo)
        return result

    @property
    def choices(self):
        return self._choices

    @choices.setter
    def choices(self, value):
        
        
        self._choices = self.widget.choices = normalize_choices(value)

    def to_python(self, value):
        
        if value in self.empty_values:
            return ""
        return str(value)

    def validate(self, value):
        
        super().validate(value)
        if value and not self.valid_value(value):
            raise ValidationError(
                self.error_messages["invalid_choice"],
                code="invalid_choice",
                params={"value": value},
            )

    def valid_value(self, value):
        
        text_value = str(value)
        for k, v in self.choices:
            if isinstance(v, (list, tuple)):
                
                for k2, v2 in v:
                    if value == k2 or text_value == str(k2):
                        return True
            else:
                if value == k or text_value == str(k):
                    return True
        return False


class TypedChoiceField(ChoiceField):
    def __init__(self, *, coerce=lambda val: val, empty_value="", **kwargs):
        self.coerce = coerce
        self.empty_value = empty_value
        super().__init__(**kwargs)

    def _coerce(self, value):
        
        if value == self.empty_value or value in self.empty_values:
            return self.empty_value
        try:
            value = self.coerce(value)
        except (ValueError, TypeError, ValidationError):
            raise ValidationError(
                self.error_messages["invalid_choice"],
                code="invalid_choice",
                params={"value": value},
            )
        return value

    def clean(self, value):
        value = super().clean(value)
        return self._coerce(value)


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."),
    }

    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"
            )
        return [str(val) for val in value]

    def validate(self, value):
        
        if self.required and not value:
            raise ValidationError(self.error_messages["required"], code="required")
        
        for val in value:
            if not self.valid_value(val):
                raise ValidationError(
                    self.error_messages["invalid_choice"],
                    code="invalid_choice",
                    params={"value": val},
                )

    def has_changed(self, initial, data):
        if self.disabled:
            return False
        if initial is None:
            initial = []
        if data is None:
            data = []
        if len(initial) != len(data):
            return True
        initial_set = {str(value) for value in initial}
        data_set = {str(value) for value in data}
        return data_set != initial_set


class TypedMultipleChoiceField(MultipleChoiceField):
    def __init__(self, *, coerce=lambda val: val, **kwargs):
        self.coerce = coerce
        self.empty_value = kwargs.pop("empty_value", [])
        super().__init__(**kwargs)

    def _coerce(self, value):
        
        if value == self.empty_value or value in self.empty_values:
            return self.empty_value
        new_value = []
        for choice in value:
            try:
                new_value.append(self.coerce(choice))
            except (ValueError, TypeError, ValidationError):
                raise ValidationError(
                    self.error_messages["invalid_choice"],
                    code="invalid_choice",
                    params={"value": choice},
                )
        return new_value

    def clean(self, value):
        value = super().clean(value)
        return self._coerce(value)

    def validate(self, value):
        if value != self.empty_value:
            super().validate(value)
        elif self.required:
            raise ValidationError(self.error_messages["required"], code="required")


class ComboField(Field):
    

    def __init__(self, fields, **kwargs):
        super().__init__(**kwargs)
        
        
        
        for f in fields:
            f.required = False
        self.fields = fields

    def clean(self, value):
        
        super().clean(value)
        for field in self.fields:
            value = field.clean(value)
        return value


class MultiValueField(Field):
    

    default_error_messages = {
        "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"])
            if self.disabled:
                f.disabled = True
            if self.require_all_fields:
                
                
                
                f.required = False
        self.fields = fields

    def __deepcopy__(self, memo):
        result = super().__deepcopy__(memo)
        result.fields = tuple(x.__deepcopy__(memo) for x in self.fields)
        return result

    def validate(self, value):
        pass

    def clean(self, value):
        
        clean_data = []
        errors = []
        if self.disabled and not isinstance(value, list):
            value = self.widget.decompress(value)
        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"
                    )
                else:
                    return self.compress([])
        else:
            raise ValidationError(self.error_messages["invalid"], code="invalid")
        for i, field in enumerate(self.fields):
            try:
                field_value = value[i]
            except IndexError:
                field_value = None
            if field_value in self.empty_values:
                if self.require_all_fields:
                    
                    
                    if self.required:
                        raise ValidationError(
                            self.error_messages["required"], code="required"
                        )
                elif field.required:
                    
                    
                    
                    if field.error_messages["incomplete"] not in errors:
                        errors.append(field.error_messages["incomplete"])
                    continue
            try:
                clean_data.append(field.clean(field_value))
            except ValidationError as e:
                
                
                
                errors.extend(m for m in e.error_list if m not in errors)
        if errors:
            raise ValidationError(errors)

        out = self.compress(clean_data)
        self.validate(out)
        self.run_validators(out)
        return out

    def compress(self, data_list):
        
        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))]
        else:
            if not isinstance(initial, list):
                initial = self.widget.decompress(initial)
        for field, initial, data in zip(self.fields, initial, data):
            try:
                initial = field.to_python(initial)
            except ValidationError:
                return True
            if field.has_changed(initial, data):
                return True
        return False


class FilePathField(ChoiceField):
    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)

        if self.required:
            self.choices = []
        else:
            self.choices = [("", "---------")]

        if self.match is not None:
            self.match_re = re.compile(self.match)

        if recursive:
            for root, dirs, files in sorted(os.walk(self.path)):
                if self.allow_files:
                    for f in sorted(files):
                        if self.match is None or self.match_re.search(f):
                            f = os.path.join(root, f)
                            self.choices.append((f, f.replace(path, "", 1)))
                if self.allow_folders:
                    for f in sorted(dirs):
                        if f == "__pycache__":
                            continue
                        if self.match is None or self.match_re.search(f):
                            f = os.path.join(root, f)
                            self.choices.append((f, f.replace(path, "", 1)))
        else:
            choices = []
            with os.scandir(self.path) as entries:
                for f in entries:
                    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)):
                        choices.append((f.path, f.name))
            choices.sort(key=operator.itemgetter(1))
            self.choices.extend(choices)

        self.widget.choices = self.choices


class SplitDateTimeField(MultiValueField):
    widget = SplitDateTimeWidget
    hidden_widget = SplitHiddenDateTimeWidget
    default_error_messages = {
        "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)
        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,
            ),
        )
        super().__init__(fields, **kwargs)

    def compress(self, data_list):
        if data_list:
            
            
            if data_list[0] in self.empty_values:
                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"
                )
            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):
        self.unpack_ipv4 = unpack_ipv4
        self.default_validators = validators.ip_address_validators(
            protocol, unpack_ipv4
        )
        super().__init__(**kwargs)

    def to_python(self, value):
        if value in self.empty_values:
            return ""
        value = value.strip()
        if value and ":" in value:
            return clean_ipv6_address(value, self.unpack_ipv4)
        return value


class SlugField(CharField):
    default_validators = [validators.validate_slug]

    def __init__(self, *, allow_unicode=False, **kwargs):
        self.allow_unicode = allow_unicode
        if self.allow_unicode:
            self.default_validators = [validators.validate_unicode_slug]
        super().__init__(**kwargs)


class UUIDField(CharField):
    default_error_messages = {
        "invalid": _("Enter a valid UUID."),
    }

    def prepare_value(self, value):
        if isinstance(value, uuid.UUID):
            return str(value)
        return value

    def to_python(self, value):
        value = super().to_python(value)
        if value in self.empty_values:
            return None
        if not isinstance(value, uuid.UUID):
            try:
                value = uuid.UUID(value)
            except ValueError:
                raise ValidationError(self.error_messages["invalid"], code="invalid")
        return value


class InvalidJSONInput(str):
    pass


class JSONString(str):
    pass


class JSONField(CharField):
    default_error_messages = {
        "invalid": _("Enter a valid JSON."),
    }
    widget = Textarea

    def __init__(self, encoder=None, decoder=None, **kwargs):
        self.encoder = encoder
        self.decoder = decoder
        super().__init__(**kwargs)

    def to_python(self, value):
        if self.disabled:
            return value
        if value in self.empty_values:
            return None
        elif isinstance(value, (list, dict, int, float, JSONString)):
            return value
        try:
            converted = json.loads(value, cls=self.decoder)
        except json.JSONDecodeError:
            raise ValidationError(
                self.error_messages["invalid"],
                code="invalid",
                params={"value": value},
            )
        if isinstance(converted, str):
            return JSONString(converted)
        else:
            return converted

    def bound_data(self, data, initial):
        if self.disabled:
            return initial
        if data is None:
            return None
        try:
            return json.loads(data, cls=self.decoder)
        except json.JSONDecodeError:
            return InvalidJSONInput(data)

    def prepare_value(self, value):
        if isinstance(value, InvalidJSONInput):
            return value
        return json.dumps(value, ensure_ascii=False, cls=self.encoder)

    def has_changed(self, initial, data):
        if super().has_changed(initial, data):
            return True
        
        
        return json.dumps(initial, sort_keys=True, cls=self.encoder) != json.dumps(
            self.to_python(data), sort_keys=True, cls=self.encoder
        )