summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDulmandakh <dulmandakh@gmail.com>2019-11-06 18:22:15 +0800
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2019-11-14 22:27:29 +0100
commit24b9f5082344a127147266dd52d5d2dcd1c9cb44 (patch)
treeae14635dc1d1dc8d3d31ef84105ce7c16d69cf2c
parentefc3e32d6d7fb9bb41be73b80c8607b653c1fbd6 (diff)
Fixed #29916 -- Added lower_inc, lower_inf, upper_inc, and upper_inf lookups for RangeFields.
Co-Authored-By: Mariusz Felisiak <felisiak.mariusz@gmail.com>
-rw-r--r--django/contrib/postgres/fields/ranges.py28
-rw-r--r--docs/ref/contrib/postgres/fields.txt56
-rw-r--r--docs/releases/3.1.txt4
-rw-r--r--tests/postgres_tests/test_ranges.py24
4 files changed, 112 insertions, 0 deletions
diff --git a/django/contrib/postgres/fields/ranges.py b/django/contrib/postgres/fields/ranges.py
index c0d985afa3..77a15632eb 100644
--- a/django/contrib/postgres/fields/ranges.py
+++ b/django/contrib/postgres/fields/ranges.py
@@ -284,3 +284,31 @@ class IsEmpty(models.Transform):
lookup_name = 'isempty'
function = 'isempty'
output_field = models.BooleanField()
+
+
+@RangeField.register_lookup
+class LowerInclusive(models.Transform):
+ lookup_name = 'lower_inc'
+ function = 'LOWER_INC'
+ output_field = models.BooleanField()
+
+
+@RangeField.register_lookup
+class LowerInfinite(models.Transform):
+ lookup_name = 'lower_inf'
+ function = 'LOWER_INF'
+ output_field = models.BooleanField()
+
+
+@RangeField.register_lookup
+class UpperInclusive(models.Transform):
+ lookup_name = 'upper_inc'
+ function = 'UPPER_INC'
+ output_field = models.BooleanField()
+
+
+@RangeField.register_lookup
+class UpperInfinite(models.Transform):
+ lookup_name = 'upper_inf'
+ function = 'UPPER_INF'
+ output_field = models.BooleanField()
diff --git a/docs/ref/contrib/postgres/fields.txt b/docs/ref/contrib/postgres/fields.txt
index 27cf6469f1..cd35a33ee6 100644
--- a/docs/ref/contrib/postgres/fields.txt
+++ b/docs/ref/contrib/postgres/fields.txt
@@ -864,6 +864,62 @@ Returned objects are empty ranges. Can be chained to valid lookups for a
>>> Event.objects.filter(ages__isempty=True)
<QuerySet []>
+.. fieldlookup:: rangefield.lower_inc
+
+``lower_inc``
+^^^^^^^^^^^^^
+
+.. versionadded:: 3.1
+
+Returns objects that have inclusive or exclusive lower bounds, depending on the
+boolean value passed. Can be chained to valid lookups for a
+:class:`~django.db.models.BooleanField`.
+
+ >>> Event.objects.filter(ages__lower_inc=True)
+ <QuerySet [<Event: Soft play>, <Event: Pub trip>]>
+
+.. fieldlookup:: rangefield.lower_inf
+
+``lower_inf``
+^^^^^^^^^^^^^
+
+.. versionadded:: 3.1
+
+Returns objects that have unbounded (infinite) or bounded lower bound,
+depending on the boolean value passed. Can be chained to valid lookups for a
+:class:`~django.db.models.BooleanField`.
+
+ >>> Event.objects.filter(ages__lower_inf=True)
+ <QuerySet []>
+
+.. fieldlookup:: rangefield.upper_inc
+
+``upper_inc``
+^^^^^^^^^^^^^
+
+.. versionadded:: 3.1
+
+Returns objects that have inclusive or exclusive upper bounds, depending on the
+boolean value passed. Can be chained to valid lookups for a
+:class:`~django.db.models.BooleanField`.
+
+ >>> Event.objects.filter(ages__upper_inc=True)
+ <QuerySet []>
+
+.. fieldlookup:: rangefield.upper_inf
+
+``upper_inf``
+^^^^^^^^^^^^^
+
+.. versionadded:: 3.1
+
+Returns objects that have unbounded (infinite) or bounded upper bound,
+depending on the boolean value passed. Can be chained to valid lookups for a
+:class:`~django.db.models.BooleanField`.
+
+ >>> Event.objects.filter(ages__upper_inf=True)
+ <QuerySet [<Event: Pub trip>]>
+
Defining your own range types
-----------------------------
diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt
index f725e4e056..a7c9caa31e 100644
--- a/docs/releases/3.1.txt
+++ b/docs/releases/3.1.txt
@@ -80,6 +80,10 @@ Minor features
:class:`~django.contrib.postgres.fields.ArrayField` and
:class:`~django.contrib.postgres.fields.RangeField`.
+* The new :lookup:`rangefield.lower_inc`, :lookup:`rangefield.lower_inf`,
+ :lookup:`rangefield.upper_inc`, and :lookup:`rangefield.upper_inf` allows
+ querying :class:`~django.contrib.postgres.fields.RangeField` by a bound type.
+
:mod:`django.contrib.redirects`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/postgres_tests/test_ranges.py b/tests/postgres_tests/test_ranges.py
index 326015e46d..789ff3d546 100644
--- a/tests/postgres_tests/test_ranges.py
+++ b/tests/postgres_tests/test_ranges.py
@@ -296,6 +296,30 @@ class TestQuerying(PostgreSQLTestCase):
[self.objs[0], self.objs[1]],
)
+ def test_bound_type(self):
+ decimals = RangesModel.objects.bulk_create([
+ RangesModel(decimals=NumericRange(None, 10)),
+ RangesModel(decimals=NumericRange(10, None)),
+ RangesModel(decimals=NumericRange(5, 15)),
+ RangesModel(decimals=NumericRange(5, 15, '(]')),
+ ])
+ tests = [
+ ('lower_inc', True, [decimals[1], decimals[2]]),
+ ('lower_inc', False, [decimals[0], decimals[3]]),
+ ('lower_inf', True, [decimals[0]]),
+ ('lower_inf', False, [decimals[1], decimals[2], decimals[3]]),
+ ('upper_inc', True, [decimals[3]]),
+ ('upper_inc', False, [decimals[0], decimals[1], decimals[2]]),
+ ('upper_inf', True, [decimals[1]]),
+ ('upper_inf', False, [decimals[0], decimals[2], decimals[3]]),
+ ]
+ for lookup, filter_arg, excepted_result in tests:
+ with self.subTest(lookup=lookup, filter_arg=filter_arg):
+ self.assertSequenceEqual(
+ RangesModel.objects.filter(**{'decimals__%s' % lookup: filter_arg}),
+ excepted_result,
+ )
+
class TestQueryingWithRanges(PostgreSQLTestCase):
def test_date_range(self):