Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ranges.py 3.0KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. from psycopg2.extras import DateRange, DateTimeTZRange, NumericRange
  2. from django import forms
  3. from django.core import exceptions
  4. from django.forms.widgets import MultiWidget
  5. from django.utils.translation import gettext_lazy as _
  6. __all__ = [
  7. 'BaseRangeField', 'IntegerRangeField', 'FloatRangeField',
  8. 'DateTimeRangeField', 'DateRangeField', 'RangeWidget',
  9. ]
  10. class BaseRangeField(forms.MultiValueField):
  11. default_error_messages = {
  12. 'invalid': _('Enter two valid values.'),
  13. 'bound_ordering': _('The start of the range must not exceed the end of the range.'),
  14. }
  15. def __init__(self, **kwargs):
  16. if 'widget' not in kwargs:
  17. kwargs['widget'] = RangeWidget(self.base_field.widget)
  18. if 'fields' not in kwargs:
  19. kwargs['fields'] = [self.base_field(required=False), self.base_field(required=False)]
  20. kwargs.setdefault('required', False)
  21. kwargs.setdefault('require_all_fields', False)
  22. super().__init__(**kwargs)
  23. def prepare_value(self, value):
  24. lower_base, upper_base = self.fields
  25. if isinstance(value, self.range_type):
  26. return [
  27. lower_base.prepare_value(value.lower),
  28. upper_base.prepare_value(value.upper),
  29. ]
  30. if value is None:
  31. return [
  32. lower_base.prepare_value(None),
  33. upper_base.prepare_value(None),
  34. ]
  35. return value
  36. def compress(self, values):
  37. if not values:
  38. return None
  39. lower, upper = values
  40. if lower is not None and upper is not None and lower > upper:
  41. raise exceptions.ValidationError(
  42. self.error_messages['bound_ordering'],
  43. code='bound_ordering',
  44. )
  45. try:
  46. range_value = self.range_type(lower, upper)
  47. except TypeError:
  48. raise exceptions.ValidationError(
  49. self.error_messages['invalid'],
  50. code='invalid',
  51. )
  52. else:
  53. return range_value
  54. class IntegerRangeField(BaseRangeField):
  55. default_error_messages = {'invalid': _('Enter two whole numbers.')}
  56. base_field = forms.IntegerField
  57. range_type = NumericRange
  58. class FloatRangeField(BaseRangeField):
  59. default_error_messages = {'invalid': _('Enter two numbers.')}
  60. base_field = forms.FloatField
  61. range_type = NumericRange
  62. class DateTimeRangeField(BaseRangeField):
  63. default_error_messages = {'invalid': _('Enter two valid date/times.')}
  64. base_field = forms.DateTimeField
  65. range_type = DateTimeTZRange
  66. class DateRangeField(BaseRangeField):
  67. default_error_messages = {'invalid': _('Enter two valid dates.')}
  68. base_field = forms.DateField
  69. range_type = DateRange
  70. class RangeWidget(MultiWidget):
  71. def __init__(self, base_widget, attrs=None):
  72. widgets = (base_widget, base_widget)
  73. super().__init__(widgets, attrs)
  74. def decompress(self, value):
  75. if value:
  76. return (value.lower, value.upper)
  77. return (None, None)