From 7174cf0b009711c41e5f76f6c0c68137898716a0 Mon Sep 17 00:00:00 2001
From: Mads Jensen <mje@inducks.org>
Date: Fri, 12 Jul 2019 13:08:00 +0200
Subject: [PATCH] Refs #29824 -- Added RangeOperators helper class.

---
 django/contrib/postgres/fields/ranges.py | 29 ++++++++++++++++++------
 docs/ref/contrib/postgres/fields.txt     | 28 +++++++++++++++++++++++
 docs/releases/3.0.txt                    |  4 +++-
 3 files changed, 53 insertions(+), 8 deletions(-)

diff --git a/django/contrib/postgres/fields/ranges.py b/django/contrib/postgres/fields/ranges.py
index 9a8932bcee..21e982cedd 100644
--- a/django/contrib/postgres/fields/ranges.py
+++ b/django/contrib/postgres/fields/ranges.py
@@ -12,9 +12,24 @@ __all__ = [
     'RangeField', 'IntegerRangeField', 'BigIntegerRangeField',
     'DecimalRangeField', 'DateTimeRangeField', 'DateRangeField',
     'FloatRangeField',
+    'RangeOperators',
 ]
 
 
+class RangeOperators:
+    # https://www.postgresql.org/docs/current/functions-range.html#RANGE-OPERATORS-TABLE
+    EQUAL = '='
+    NOT_EQUAL = '<>'
+    CONTAINS = '@>'
+    CONTAINED_BY = '<@'
+    OVERLAPS = '&&'
+    FULLY_LT = '<<'
+    FULLY_GT = '>>'
+    NOT_LT = '&>'
+    NOT_GT = '&<'
+    ADJACENT_TO = '-|-'
+
+
 class RangeField(models.Field):
     empty_strings_allowed = False
 
@@ -155,7 +170,7 @@ class DateTimeRangeContains(lookups.PostgresSimpleLookup):
     type.
     """
     lookup_name = 'contains'
-    operator = '@>'
+    operator = RangeOperators.CONTAINS
 
     def process_rhs(self, compiler, connection):
         # Transform rhs value for db lookup.
@@ -193,7 +208,7 @@ class RangeContainedBy(lookups.PostgresSimpleLookup):
         'date': 'daterange',
         'timestamp with time zone': 'tstzrange',
     }
-    operator = '<@'
+    operator = RangeOperators.CONTAINED_BY
 
     def process_rhs(self, compiler, connection):
         rhs, rhs_params = super().process_rhs(compiler, connection)
@@ -220,31 +235,31 @@ models.FloatField.register_lookup(RangeContainedBy)
 @RangeField.register_lookup
 class FullyLessThan(lookups.PostgresSimpleLookup):
     lookup_name = 'fully_lt'
-    operator = '<<'
+    operator = RangeOperators.FULLY_LT
 
 
 @RangeField.register_lookup
 class FullGreaterThan(lookups.PostgresSimpleLookup):
     lookup_name = 'fully_gt'
-    operator = '>>'
+    operator = RangeOperators.FULLY_GT
 
 
 @RangeField.register_lookup
 class NotLessThan(lookups.PostgresSimpleLookup):
     lookup_name = 'not_lt'
-    operator = '&>'
+    operator = RangeOperators.NOT_LT
 
 
 @RangeField.register_lookup
 class NotGreaterThan(lookups.PostgresSimpleLookup):
     lookup_name = 'not_gt'
-    operator = '&<'
+    operator = RangeOperators.NOT_GT
 
 
 @RangeField.register_lookup
 class AdjacentToLookup(lookups.PostgresSimpleLookup):
     lookup_name = 'adjacent_to'
-    operator = '-|-'
+    operator = RangeOperators.ADJACENT_TO
 
 
 @RangeField.register_lookup
diff --git a/docs/ref/contrib/postgres/fields.txt b/docs/ref/contrib/postgres/fields.txt
index 34c4a4892d..387eb0f02e 100644
--- a/docs/ref/contrib/postgres/fields.txt
+++ b/docs/ref/contrib/postgres/fields.txt
@@ -916,3 +916,31 @@ types.
     .. attribute:: range_type
 
         The psycopg2 range type to use.
+
+Range operators
+---------------
+
+.. versionadded:: 3.0
+
+.. class:: RangeOperators
+
+PostgreSQL provides a set of SQL operators that can be used together with the
+range data types (see `the PostgreSQL documentation for the full details of
+range operators <https://www.postgresql.org/docs/current/
+functions-range.html#RANGE-OPERATORS-TABLE>`_). This class is meant as a
+convenient method to avoid typos. The operator names overlap with the names of
+corresponding lookups.
+
+.. code-block:: python
+
+    class RangeOperators:
+        EQUAL = '='
+        NOT_EQUAL = '<>'
+        CONTAINS = '@>'
+        CONTAINED_BY = '<@'
+        OVERLAPS = '&&'
+        FULLY_LT = '<<'
+        FULLY_GT = '>>'
+        NOT_LT = '&>'
+        NOT_GT = '&<'
+        ADJACENT_TO = '-|-'
diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt
index 5a1279219e..6aa21975bf 100644
--- a/docs/releases/3.0.txt
+++ b/docs/releases/3.0.txt
@@ -133,7 +133,9 @@ Minor features
 :mod:`django.contrib.postgres`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-* ...
+* The new :class:`~django.contrib.postgres.fields.RangeOperators` helps to
+  avoid typos in SQL operators that can be used together with
+  :class:`~django.contrib.postgres.fields.RangeField`.
 
 :mod:`django.contrib.redirects`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~