summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorGuilherme Martins Crocetti <gmcrocetti@gmail.com>2021-06-17 18:13:49 -0300
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-11-04 19:08:57 +0100
commitfc565cb539e4c1e5fba70d9ebb19bac0ca251d37 (patch)
tree92bcd7957c5c5090748859253aa4eb7b9944cdf8 /django
parent52f6927d7fb7a4dca40afce0391d018b4c34dd6d (diff)
Fixed #27147 -- Allowed specifying bounds of tuple inputs for non-discrete range fields.
Diffstat (limited to 'django')
-rw-r--r--django/contrib/postgres/fields/ranges.py39
-rw-r--r--django/contrib/postgres/forms/ranges.py5
2 files changed, 41 insertions, 3 deletions
diff --git a/django/contrib/postgres/fields/ranges.py b/django/contrib/postgres/fields/ranges.py
index 8eab2cd2d9..b395e213a1 100644
--- a/django/contrib/postgres/fields/ranges.py
+++ b/django/contrib/postgres/fields/ranges.py
@@ -44,6 +44,10 @@ class RangeField(models.Field):
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
+ if 'default_bounds' in kwargs:
+ raise TypeError(
+ f"Cannot use 'default_bounds' with {self.__class__.__name__}."
+ )
# Initializing base_field here ensures that its model matches the model for self.
if hasattr(self, 'base_field'):
self.base_field = self.base_field()
@@ -112,6 +116,37 @@ class RangeField(models.Field):
return super().formfield(**kwargs)
+CANONICAL_RANGE_BOUNDS = '[)'
+
+
+class ContinuousRangeField(RangeField):
+ """
+ Continuous range field. It allows specifying default bounds for list and
+ tuple inputs.
+ """
+
+ def __init__(self, *args, default_bounds=CANONICAL_RANGE_BOUNDS, **kwargs):
+ if default_bounds not in ('[)', '(]', '()', '[]'):
+ raise ValueError("default_bounds must be one of '[)', '(]', '()', or '[]'.")
+ self.default_bounds = default_bounds
+ super().__init__(*args, **kwargs)
+
+ def get_prep_value(self, value):
+ if isinstance(value, (list, tuple)):
+ return self.range_type(value[0], value[1], self.default_bounds)
+ return super().get_prep_value(value)
+
+ def formfield(self, **kwargs):
+ kwargs.setdefault('default_bounds', self.default_bounds)
+ return super().formfield(**kwargs)
+
+ def deconstruct(self):
+ name, path, args, kwargs = super().deconstruct()
+ if self.default_bounds and self.default_bounds != CANONICAL_RANGE_BOUNDS:
+ kwargs['default_bounds'] = self.default_bounds
+ return name, path, args, kwargs
+
+
class IntegerRangeField(RangeField):
base_field = models.IntegerField
range_type = NumericRange
@@ -130,7 +165,7 @@ class BigIntegerRangeField(RangeField):
return 'int8range'
-class DecimalRangeField(RangeField):
+class DecimalRangeField(ContinuousRangeField):
base_field = models.DecimalField
range_type = NumericRange
form_field = forms.DecimalRangeField
@@ -139,7 +174,7 @@ class DecimalRangeField(RangeField):
return 'numrange'
-class DateTimeRangeField(RangeField):
+class DateTimeRangeField(ContinuousRangeField):
base_field = models.DateTimeField
range_type = DateTimeTZRange
form_field = forms.DateTimeRangeField
diff --git a/django/contrib/postgres/forms/ranges.py b/django/contrib/postgres/forms/ranges.py
index 5a20975eb4..9c673ab40c 100644
--- a/django/contrib/postgres/forms/ranges.py
+++ b/django/contrib/postgres/forms/ranges.py
@@ -42,6 +42,9 @@ class BaseRangeField(forms.MultiValueField):
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):
@@ -68,7 +71,7 @@ class BaseRangeField(forms.MultiValueField):
code='bound_ordering',
)
try:
- range_value = self.range_type(lower, upper)
+ range_value = self.range_type(lower, upper, **self.range_kwargs)
except TypeError:
raise exceptions.ValidationError(
self.error_messages['invalid'],