Source code for django.contrib.postgres.forms.ranges

import warnings

from psycopg2.extras import DateRange, DateTimeTZRange, NumericRange

from django import forms
from django.core import exceptions
from django.forms.widgets import HiddenInput, MultiWidget
from django.utils.deprecation import RemovedInDjango31Warning
from django.utils.translation import gettext_lazy as _

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


[docs]class RangeWidget(MultiWidget): def __init__(self, base_widget, attrs=None): widgets = (base_widget, base_widget) super().__init__(widgets, attrs)
[docs] def decompress(self, value): if value: return (value.lower, value.upper) return (None, None)
class HiddenRangeWidget(RangeWidget): """A widget that splits input into two <input type="hidden"> inputs.""" 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) 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) except TypeError: raise exceptions.ValidationError( self.error_messages['invalid'], code='invalid', ) else: return range_value
[docs]class IntegerRangeField(BaseRangeField): default_error_messages = {'invalid': _('Enter two whole numbers.')} base_field = forms.IntegerField range_type = NumericRange
[docs]class DecimalRangeField(BaseRangeField): default_error_messages = {'invalid': _('Enter two numbers.')} base_field = forms.DecimalField range_type = NumericRange
[docs]class FloatRangeField(DecimalRangeField): base_field = forms.FloatField def __init__(self, **kwargs): warnings.warn( 'FloatRangeField is deprecated in favor of DecimalRangeField.', RemovedInDjango31Warning, stacklevel=2, ) super().__init__(**kwargs)
[docs]class DateTimeRangeField(BaseRangeField): default_error_messages = {'invalid': _('Enter two valid date/times.')} base_field = forms.DateTimeField range_type = DateTimeTZRange
[docs]class DateRangeField(BaseRangeField): default_error_messages = {'invalid': _('Enter two valid dates.')} base_field = forms.DateField range_type = DateRange