diff options
| author | Guilherme Martins Crocetti <gmcrocetti@gmail.com> | 2021-06-17 18:13:49 -0300 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-11-04 19:08:57 +0100 |
| commit | fc565cb539e4c1e5fba70d9ebb19bac0ca251d37 (patch) | |
| tree | 92bcd7957c5c5090748859253aa4eb7b9944cdf8 /django | |
| parent | 52f6927d7fb7a4dca40afce0391d018b4c34dd6d (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.py | 39 | ||||
| -rw-r--r-- | django/contrib/postgres/forms/ranges.py | 5 |
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'], |
