Преглед изворни кода

Fixed #29916 -- Added lower_inc, lower_inf, upper_inc, and upper_inf lookups for RangeFields.

Co-Authored-By: Mariusz Felisiak <felisiak.mariusz@gmail.com>
Dulmandakh пре 5 година
родитељ
комит
24b9f50823

+ 28 - 0
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()

+ 56 - 0
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
 -----------------------------
 

+ 4 - 0
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`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 24 - 0
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):