a widget that splits input into two input typehidden inputs def initself attrsnone super inithiddeninput attrs class baserangefieldforms multivaluefield defaulterrormessages invalid enter two valid values boundordering the start of the range must not exceed the end of the range hiddenwidget hiddenrangewidget def initself kwargs if widget not in kwargs kwargswidget rangewidgetself basefield widget if fields not in kwargs kwargsfields self basefieldrequiredfalse self basefieldrequiredfalse kwargs setdefaultrequired false kwargs setdefaultrequireallfields false self rangekwargs if defaultbounds kwargs popdefaultbounds none self rangekwargs bounds defaultbounds super initkwargs def preparevalueself value lowerbase upperbase self fields if isinstancevalue self rangetype return lowerbase preparevaluevalue lower upperbase preparevaluevalue upper if value is none return lowerbase preparevaluenone upperbase preparevaluenone return value def compressself values if not values return none lower upper values if lower is not none and upper is not none and lower upper raise exceptions validationerror self errormessagesboundordering codeboundordering try rangevalue self rangetypelower upper self rangekwargs except typeerror raise exceptions validationerror self errormessagesinvalid codeinvalid else return rangevalue class integerrangefieldbaserangefield defaulterrormessages invalid enter two whole numbers basefield forms integerfield rangetype numericrange class decimalrangefieldbaserangefield defaulterrormessages invalid enter two numbers basefield forms decimalfield rangetype numericrange class datetimerangefieldbaserangefield defaulterrormessages invalid enter two valid datetimes basefield forms datetimefield rangetype datetimetzrange class daterangefieldbaserangefield defaulterrormessages invalid enter two valid dates basefield forms datefield rangetype daterange a widget that splits input into two input type hidden inputs
from django import forms
from django.core import exceptions
from django.db.backends.postgresql.psycopg_any import (
    DateRange,
    DateTimeTZRange,
    NumericRange,
)
from django.forms.widgets import HiddenInput, MultiWidget
from django.utils.translation import gettext_lazy as _

__all__ = [
    "BaseRangeField",
    "IntegerRangeField",
    "DecimalRangeField",
    "DateTimeRangeField",
    "DateRangeField",
    "HiddenRangeWidget",
    "RangeWidget",
]


class RangeWidget(MultiWidget):
    def __init__(self, base_widget, attrs=None):
        widgets = (base_widget, base_widget)
        super().__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            return (value.lower, value.upper)
        return (None, None)


class HiddenRangeWidget(RangeWidget):
    

    def __init__(self, attrs=None):
        super().__init__(HiddenInput, attrs)


class BaseRangeField(forms.MultiValueField):
    default_error_messages = {
        "invalid": _("Enter two valid values."),
        "bound_ordering": _(
            "The start of the range must not exceed the end of the range."
        ),
    }
    hidden_widget = HiddenRangeWidget

    def __init__(self, **kwargs):
        if "widget" not in kwargs:
            kwargs["widget"] = RangeWidget(self.base_field.widget)
        if "fields" not in kwargs:
            kwargs["fields"] = [
                self.base_field(required=False),
                self.base_field(required=False),
            ]
        kwargs.setdefault("required", False)
        kwargs.setdefault("require_all_fields", False)
        self.range_kwargs = {}
        if default_bounds := kwargs.pop("default_bounds", None):
            self.range_kwargs = {"bounds": default_bounds}
        super().__init__(**kwargs)

    def prepare_value(self, value):
        lower_base, upper_base = self.fields
        if isinstance(value, self.range_type):
            return [
                lower_base.prepare_value(value.lower),
                upper_base.prepare_value(value.upper),
            ]
        if value is None:
            return [
                lower_base.prepare_value(None),
                upper_base.prepare_value(None),
            ]
        return value

    def compress(self, values):
        if not values:
            return None
        lower, upper = values
        if lower is not None and upper is not None and lower > upper:
            raise exceptions.ValidationError(
                self.error_messages["bound_ordering"],
                code="bound_ordering",
            )
        try:
            range_value = self.range_type(lower, upper, **self.range_kwargs)
        except TypeError:
            raise exceptions.ValidationError(
                self.error_messages["invalid"],
                code="invalid",
            )
        else:
            return range_value


class IntegerRangeField(BaseRangeField):
    default_error_messages = {"invalid": _("Enter two whole numbers.")}
    base_field = forms.IntegerField
    range_type = NumericRange


class DecimalRangeField(BaseRangeField):
    default_error_messages = {"invalid": _("Enter two numbers.")}
    base_field = forms.DecimalField
    range_type = NumericRange


class DateTimeRangeField(BaseRangeField):
    default_error_messages = {"invalid": _("Enter two valid date/times.")}
    base_field = forms.DateTimeField
    range_type = DateTimeTZRange


class DateRangeField(BaseRangeField):
    default_error_messages = {"invalid": _("Enter two valid dates.")}
    base_field = forms.DateField
    range_type = DateRange