summaryrefslogtreecommitdiff
path: root/docs/ref/contrib/postgres/fields.txt
blob: b81a49d4eab474a7e24c24208be4c6c9ae433d0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
================================
PostgreSQL specific model fields
================================

All of these fields are available from the ``django.contrib.postgres.fields``
module.

.. currentmodule:: django.contrib.postgres.fields

Indexing these fields
=====================

:class:`~django.db.models.Index` and :attr:`.Field.db_index` both create a
B-tree index, which isn't particularly helpful when querying complex data types.
Indexes such as :class:`~django.contrib.postgres.indexes.GinIndex` and
:class:`~django.contrib.postgres.indexes.GistIndex` are better suited, though
the index choice is dependent on the queries that you're using. Generally, GiST
may be a good choice for the :ref:`range fields <range-fields>` and
:class:`HStoreField`, and GIN may be helpful for :class:`ArrayField`.

``ArrayField``
==============

.. class:: ArrayField(base_field, size=None, **options)

    A field for storing lists of data. Most field types can be used, and you
    pass another field instance as the :attr:`base_field
    <ArrayField.base_field>`. You may also specify a :attr:`size
    <ArrayField.size>`. ``ArrayField`` can be nested to store multi-dimensional
    arrays.

    If you give the field a :attr:`~django.db.models.Field.default`, ensure
    it's a callable such as ``list`` (for an empty default) or a callable that
    returns a list (such as a function). Incorrectly using ``default=[]``
    creates a mutable default that is shared between all instances of
    ``ArrayField``.

    .. attribute:: base_field

        This is a required argument.

        Specifies the underlying data type and behavior for the array. It
        should be an instance of a subclass of
        :class:`~django.db.models.Field`. For example, it could be an
        :class:`~django.db.models.IntegerField` or a
        :class:`~django.db.models.CharField`. Most field types are permitted,
        with the exception of those handling relational data
        (:class:`~django.db.models.ForeignKey`,
        :class:`~django.db.models.OneToOneField` and
        :class:`~django.db.models.ManyToManyField`).

        It is possible to nest array fields - you can specify an instance of
        ``ArrayField`` as the ``base_field``. For example::

            from django.contrib.postgres.fields import ArrayField
            from django.db import models

            class ChessBoard(models.Model):
                board = ArrayField(
                    ArrayField(
                        models.CharField(max_length=10, blank=True),
                        size=8,
                    ),
                    size=8,
                )

        Transformation of values between the database and the model, validation
        of data and configuration, and serialization are all delegated to the
        underlying base field.

    .. attribute:: size

        This is an optional argument.

        If passed, the array will have a maximum size as specified. This will
        be passed to the database, although PostgreSQL at present does not
        enforce the restriction.

.. note::

    When nesting ``ArrayField``, whether you use the ``size`` parameter or not,
    PostgreSQL requires that the arrays are rectangular::

        from django.contrib.postgres.fields import ArrayField
        from django.db import models

        class Board(models.Model):
            pieces = ArrayField(ArrayField(models.IntegerField()))

        # Valid
        Board(pieces=[
            [2, 3],
            [2, 1],
        ])

        # Not valid
        Board(pieces=[
            [2, 3],
            [2],
        ])

    If irregular shapes are required, then the underlying field should be made
    nullable and the values padded with ``None``.

Querying ``ArrayField``
-----------------------

There are a number of custom lookups and transforms for :class:`ArrayField`.
We will use the following example model::

    from django.contrib.postgres.fields import ArrayField
    from django.db import models

    class Post(models.Model):
        name = models.CharField(max_length=200)
        tags = ArrayField(models.CharField(max_length=200), blank=True)

        def __str__(self):
            return self.name

.. fieldlookup:: arrayfield.contains

``contains``
~~~~~~~~~~~~

The :lookup:`contains` lookup is overridden on :class:`ArrayField`. The
returned objects will be those where the values passed are a subset of the
data. It uses the SQL operator ``@>``. For example::

    >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
    >>> Post.objects.create(name='Second post', tags=['thoughts'])
    >>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])

    >>> Post.objects.filter(tags__contains=['thoughts'])
    <QuerySet [<Post: First post>, <Post: Second post>]>

    >>> Post.objects.filter(tags__contains=['django'])
    <QuerySet [<Post: First post>, <Post: Third post>]>

    >>> Post.objects.filter(tags__contains=['django', 'thoughts'])
    <QuerySet [<Post: First post>]>

.. fieldlookup:: arrayfield.contained_by

``contained_by``
~~~~~~~~~~~~~~~~

This is the inverse of the :lookup:`contains <arrayfield.contains>` lookup -
the objects returned will be those where the data is a subset of the values
passed. It uses the SQL operator ``<@``. For example::

    >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
    >>> Post.objects.create(name='Second post', tags=['thoughts'])
    >>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])

    >>> Post.objects.filter(tags__contained_by=['thoughts', 'django'])
    <QuerySet [<Post: First post>, <Post: Second post>]>

    >>> Post.objects.filter(tags__contained_by=['thoughts', 'django', 'tutorial'])
    <QuerySet [<Post: First post>, <Post: Second post>, <Post: Third post>]>

.. fieldlookup:: arrayfield.overlap

``overlap``
~~~~~~~~~~~

Returns objects where the data shares any results with the values passed. Uses
the SQL operator ``&&``. For example::

    >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
    >>> Post.objects.create(name='Second post', tags=['thoughts'])
    >>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])

    >>> Post.objects.filter(tags__overlap=['thoughts'])
    <QuerySet [<Post: First post>, <Post: Second post>]>

    >>> Post.objects.filter(tags__overlap=['thoughts', 'tutorial'])
    <QuerySet [<Post: First post>, <Post: Second post>, <Post: Third post>]>

.. fieldlookup:: arrayfield.len

``len``
~~~~~~~

Returns the length of the array. The lookups available afterwards are those
available for :class:`~django.db.models.IntegerField`. For example::

    >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
    >>> Post.objects.create(name='Second post', tags=['thoughts'])

    >>> Post.objects.filter(tags__len=1)
    <QuerySet [<Post: Second post>]>

.. fieldlookup:: arrayfield.index

Index transforms
~~~~~~~~~~~~~~~~

Index transforms index into the array. Any non-negative integer can be used.
There are no errors if it exceeds the :attr:`size <ArrayField.size>` of the
array. The lookups available after the transform are those from the
:attr:`base_field <ArrayField.base_field>`. For example::

    >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
    >>> Post.objects.create(name='Second post', tags=['thoughts'])

    >>> Post.objects.filter(tags__0='thoughts')
    <QuerySet [<Post: First post>, <Post: Second post>]>

    >>> Post.objects.filter(tags__1__iexact='Django')
    <QuerySet [<Post: First post>]>

    >>> Post.objects.filter(tags__276='javascript')
    <QuerySet []>

.. note::

    PostgreSQL uses 1-based indexing for array fields when writing raw SQL.
    However these indexes and those used in :lookup:`slices <arrayfield.slice>`
    use 0-based indexing to be consistent with Python.

.. fieldlookup:: arrayfield.slice

Slice transforms
~~~~~~~~~~~~~~~~

Slice transforms take a slice of the array. Any two non-negative integers can
be used, separated by a single underscore. The lookups available after the
transform do not change. For example::

    >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
    >>> Post.objects.create(name='Second post', tags=['thoughts'])
    >>> Post.objects.create(name='Third post', tags=['django', 'python', 'thoughts'])

    >>> Post.objects.filter(tags__0_1=['thoughts'])
    <QuerySet [<Post: First post>, <Post: Second post>]>

    >>> Post.objects.filter(tags__0_2__contains=['thoughts'])
    <QuerySet [<Post: First post>, <Post: Second post>]>

.. note::

    PostgreSQL uses 1-based indexing for array fields when writing raw SQL.
    However these slices and those used in :lookup:`indexes <arrayfield.index>`
    use 0-based indexing to be consistent with Python.

.. admonition:: Multidimensional arrays with indexes and slices

    PostgreSQL has some rather esoteric behavior when using indexes and slices
    on multidimensional arrays. It will always work to use indexes to reach
    down to the final underlying data, but most other slices behave strangely
    at the database level and cannot be supported in a logical, consistent
    fashion by Django.

``CIText`` fields
=================

.. class:: CIText(**options)

    A mixin to create case-insensitive text fields backed by the citext_ type.
    Read about `the performance considerations`_ prior to using it.

    To use ``citext``, use the :class:`.CITextExtension` operation to
    :ref:`setup the citext extension <create-postgresql-extensions>` in
    PostgreSQL before the first ``CreateModel`` migration operation.

    If you're using an :class:`~django.contrib.postgres.fields.ArrayField`
    of ``CIText`` fields, you must add ``'django.contrib.postgres'`` in your
    :setting:`INSTALLED_APPS`, otherwise field values will appear as strings
    like ``'{thoughts,django}'``.

    Several fields that use the mixin are provided:

.. class:: CICharField(**options)
.. class:: CIEmailField(**options)
.. class:: CITextField(**options)

    These fields subclass :class:`~django.db.models.CharField`,
    :class:`~django.db.models.EmailField`, and
    :class:`~django.db.models.TextField`, respectively.

    ``max_length`` won't be enforced in the database since ``citext`` behaves
    similar to PostgreSQL's ``text`` type.

    .. _citext: https://www.postgresql.org/docs/current/citext.html
    .. _the performance considerations: https://www.postgresql.org/docs/current/citext.html#id-1.11.7.17.7

``HStoreField``
===============

.. class:: HStoreField(**options)

    A field for storing key-value pairs. The Python data type used is a
    ``dict``. Keys must be strings, and values may be either strings or nulls
    (``None`` in Python).

    To use this field, you'll need to:

    #. Add ``'django.contrib.postgres'`` in your :setting:`INSTALLED_APPS`.
    #. :ref:`Setup the hstore extension <create-postgresql-extensions>` in
       PostgreSQL.

    You'll see an error like ``can't adapt type 'dict'`` if you skip the first
    step, or ``type "hstore" does not exist`` if you skip the second.

.. note::

    On occasions it may be useful to require or restrict the keys which are
    valid for a given field. This can be done using the
    :class:`~django.contrib.postgres.validators.KeysValidator`.

Querying ``HStoreField``
------------------------

In addition to the ability to query by key, there are a number of custom
lookups available for ``HStoreField``.

We will use the following example model::

    from django.contrib.postgres.fields import HStoreField
    from django.db import models

    class Dog(models.Model):
        name = models.CharField(max_length=200)
        data = HStoreField()

        def __str__(self):
            return self.name

.. fieldlookup:: hstorefield.key

Key lookups
~~~~~~~~~~~

To query based on a given key, you can use that key as the lookup name::

    >>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
    >>> Dog.objects.create(name='Meg', data={'breed': 'collie'})

    >>> Dog.objects.filter(data__breed='collie')
    <QuerySet [<Dog: Meg>]>

You can chain other lookups after key lookups::

    >>> Dog.objects.filter(data__breed__contains='l')
    <QuerySet [<Dog: Rufus>, <Dog: Meg>]>

If the key you wish to query by clashes with the name of another lookup, you
need to use the :lookup:`hstorefield.contains` lookup instead.

.. warning::

    Since any string could be a key in a hstore value, any lookup other than
    those listed below will be interpreted as a key lookup. No errors are
    raised. Be extra careful for typing mistakes, and always check your queries
    work as you intend.

.. fieldlookup:: hstorefield.contains

``contains``
~~~~~~~~~~~~

The :lookup:`contains` lookup is overridden on
:class:`~django.contrib.postgres.fields.HStoreField`. The returned objects are
those where the given ``dict`` of key-value pairs are all contained in the
field. It uses the SQL operator ``@>``. For example::

    >>> Dog.objects.create(name='Rufus', data={'breed': 'labrador', 'owner': 'Bob'})
    >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
    >>> Dog.objects.create(name='Fred', data={})

    >>> Dog.objects.filter(data__contains={'owner': 'Bob'})
    <QuerySet [<Dog: Rufus>, <Dog: Meg>]>

    >>> Dog.objects.filter(data__contains={'breed': 'collie'})
    <QuerySet [<Dog: Meg>]>

.. fieldlookup:: hstorefield.contained_by

``contained_by``
~~~~~~~~~~~~~~~~

This is the inverse of the :lookup:`contains <hstorefield.contains>` lookup -
the objects returned will be those where the key-value pairs on the object are
a subset of those in the value passed. It uses the SQL operator ``<@``. For
example::

    >>> Dog.objects.create(name='Rufus', data={'breed': 'labrador', 'owner': 'Bob'})
    >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
    >>> Dog.objects.create(name='Fred', data={})

    >>> Dog.objects.filter(data__contained_by={'breed': 'collie', 'owner': 'Bob'})
    <QuerySet [<Dog: Meg>, <Dog: Fred>]>

    >>> Dog.objects.filter(data__contained_by={'breed': 'collie'})
    <QuerySet [<Dog: Fred>]>

.. fieldlookup:: hstorefield.has_key

``has_key``
~~~~~~~~~~~

Returns objects where the given key is in the data. Uses the SQL operator
``?``. For example::

    >>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
    >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})

    >>> Dog.objects.filter(data__has_key='owner')
    <QuerySet [<Dog: Meg>]>

.. fieldlookup:: hstorefield.has_any_keys

``has_any_keys``
~~~~~~~~~~~~~~~~

Returns objects where any of the given keys are in the data. Uses the SQL
operator ``?|``. For example::

    >>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
    >>> Dog.objects.create(name='Meg', data={'owner': 'Bob'})
    >>> Dog.objects.create(name='Fred', data={})

    >>> Dog.objects.filter(data__has_any_keys=['owner', 'breed'])
    <QuerySet [<Dog: Rufus>, <Dog: Meg>]>

.. fieldlookup:: hstorefield.has_keys

``has_keys``
~~~~~~~~~~~~

Returns objects where all of the given keys are in the data. Uses the SQL operator
``?&``. For example::

    >>> Dog.objects.create(name='Rufus', data={})
    >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})

    >>> Dog.objects.filter(data__has_keys=['breed', 'owner'])
    <QuerySet [<Dog: Meg>]>

.. fieldlookup:: hstorefield.keys

``keys``
~~~~~~~~

Returns objects where the array of keys is the given value. Note that the order
is not guaranteed to be reliable, so this transform is mainly useful for using
in conjunction with lookups on
:class:`~django.contrib.postgres.fields.ArrayField`. Uses the SQL function
``akeys()``. For example::

    >>> Dog.objects.create(name='Rufus', data={'toy': 'bone'})
    >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})

    >>> Dog.objects.filter(data__keys__overlap=['breed', 'toy'])
    <QuerySet [<Dog: Rufus>, <Dog: Meg>]>

.. fieldlookup:: hstorefield.values

``values``
~~~~~~~~~~

Returns objects where the array of values is the given value. Note that the
order is not guaranteed to be reliable, so this transform is mainly useful for
using in conjunction with lookups on
:class:`~django.contrib.postgres.fields.ArrayField`. Uses the SQL function
``avalues()``. For example::

    >>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
    >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})

    >>> Dog.objects.filter(data__values__contains=['collie'])
    <QuerySet [<Dog: Meg>]>

``JSONField``
=============

.. class:: JSONField(encoder=None, **options)

    A field for storing JSON encoded data. In Python the data is represented in
    its Python native format: dictionaries, lists, strings, numbers, booleans
    and ``None``.

    .. attribute:: encoder

        An optional JSON-encoding class to serialize data types not supported
        by the standard JSON serializer (``datetime``, ``uuid``, etc.). For
        example, you can use the
        :class:`~django.core.serializers.json.DjangoJSONEncoder` class or any
        other :py:class:`json.JSONEncoder` subclass.

        When the value is retrieved from the database, it will be in the format
        chosen by the custom encoder (most often a string), so you'll need to
        take extra steps to convert the value back to the initial data type
        (:meth:`Model.from_db() <django.db.models.Model.from_db>` and
        :meth:`Field.from_db_value() <django.db.models.Field.from_db_value>`
        are two possible hooks for that purpose). Your deserialization may need
        to account for the fact that you can't be certain of the input type.
        For example, you run the risk of returning a ``datetime`` that was
        actually a string that just happened to be in the same format chosen
        for ``datetime``\s.

    If you give the field a :attr:`~django.db.models.Field.default`, ensure
    it's a callable such as ``dict`` (for an empty default) or a callable that
    returns a dict (such as a function). Incorrectly using ``default={}``
    creates a mutable default that is shared between all instances of
    ``JSONField``.

.. note::

    PostgreSQL has two native JSON based data types: ``json`` and ``jsonb``.
    The main difference between them is how they are stored and how they can be
    queried. PostgreSQL's ``json`` field is stored as the original string
    representation of the JSON and must be decoded on the fly when queried
    based on keys. The ``jsonb`` field is stored based on the actual structure
    of the JSON which allows indexing. The trade-off is a small additional cost
    on writing to the ``jsonb`` field. ``JSONField`` uses ``jsonb``.

.. deprecated:: 3.1

    Use :class:`django.db.models.JSONField` instead.

Querying ``JSONField``
----------------------

See :ref:`querying-jsonfield` for details.

.. _range-fields:

Range Fields
============

There are five range field types, corresponding to the built-in range types in
PostgreSQL. These fields are used to store a range of values; for example the
start and end timestamps of an event, or the range of ages an activity is
suitable for.

All of the range fields translate to :ref:`psycopg2 Range objects
<psycopg2:adapt-range>` in Python, but also accept tuples as input if no bounds
information is necessary. The default is lower bound included, upper bound
excluded, that is ``[)`` (see the PostgreSQL documentation for details about
`different bounds`_).

.. _different bounds: https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-IO

``IntegerRangeField``
---------------------

.. class:: IntegerRangeField(**options)

    Stores a range of integers. Based on an
    :class:`~django.db.models.IntegerField`. Represented by an ``int4range`` in
    the database and a :class:`~psycopg2:psycopg2.extras.NumericRange` in
    Python.

    Regardless of the bounds specified when saving the data, PostgreSQL always
    returns a range in a canonical form that includes the lower bound and
    excludes the upper bound, that is ``[)``.

``BigIntegerRangeField``
------------------------

.. class:: BigIntegerRangeField(**options)

    Stores a range of large integers. Based on a
    :class:`~django.db.models.BigIntegerField`. Represented by an ``int8range``
    in the database and a :class:`~psycopg2:psycopg2.extras.NumericRange` in
    Python.

    Regardless of the bounds specified when saving the data, PostgreSQL always
    returns a range in a canonical form that includes the lower bound and
    excludes the upper bound, that is ``[)``.

``DecimalRangeField``
---------------------

.. class:: DecimalRangeField(**options)

    Stores a range of floating point values. Based on a
    :class:`~django.db.models.DecimalField`. Represented by a ``numrange`` in
    the database and a :class:`~psycopg2:psycopg2.extras.NumericRange` in
    Python.

``DateTimeRangeField``
----------------------

.. class:: DateTimeRangeField(**options)

    Stores a range of timestamps. Based on a
    :class:`~django.db.models.DateTimeField`. Represented by a ``tstzrange`` in
    the database and a :class:`~psycopg2:psycopg2.extras.DateTimeTZRange` in
    Python.

``DateRangeField``
------------------

.. class:: DateRangeField(**options)

    Stores a range of dates. Based on a
    :class:`~django.db.models.DateField`. Represented by a ``daterange`` in the
    database and a :class:`~psycopg2:psycopg2.extras.DateRange` in Python.

    Regardless of the bounds specified when saving the data, PostgreSQL always
    returns a range in a canonical form that includes the lower bound and
    excludes the upper bound, that is ``[)``.

Querying Range Fields
---------------------

There are a number of custom lookups and transforms for range fields. They are
available on all the above fields, but we will use the following example
model::

    from django.contrib.postgres.fields import IntegerRangeField
    from django.db import models

    class Event(models.Model):
        name = models.CharField(max_length=200)
        ages = IntegerRangeField()
        start = models.DateTimeField()

        def __str__(self):
            return self.name

We will also use the following example objects::

    >>> import datetime
    >>> from django.utils import timezone
    >>> now = timezone.now()
    >>> Event.objects.create(name='Soft play', ages=(0, 10), start=now)
    >>> Event.objects.create(name='Pub trip', ages=(21, None), start=now - datetime.timedelta(days=1))

and ``NumericRange``:

    >>> from psycopg2.extras import NumericRange

Containment functions
~~~~~~~~~~~~~~~~~~~~~

As with other PostgreSQL fields, there are three standard containment
operators: ``contains``, ``contained_by`` and ``overlap``, using the SQL
operators ``@>``, ``<@``, and ``&&`` respectively.

.. fieldlookup:: rangefield.contains

``contains``
^^^^^^^^^^^^

    >>> Event.objects.filter(ages__contains=NumericRange(4, 5))
    <QuerySet [<Event: Soft play>]>

.. fieldlookup:: rangefield.contained_by

``contained_by``
^^^^^^^^^^^^^^^^

    >>> Event.objects.filter(ages__contained_by=NumericRange(0, 15))
    <QuerySet [<Event: Soft play>]>

The ``contained_by`` lookup is also available on the non-range field types:
:class:`~django.db.models.SmallAutoField`,
:class:`~django.db.models.AutoField`, :class:`~django.db.models.BigAutoField`,
:class:`~django.db.models.SmallIntegerField`,
:class:`~django.db.models.IntegerField`,
:class:`~django.db.models.BigIntegerField`,
:class:`~django.db.models.DecimalField`, :class:`~django.db.models.FloatField`,
:class:`~django.db.models.DateField`, and
:class:`~django.db.models.DateTimeField`. For example::

    >>> from psycopg2.extras import DateTimeTZRange
    >>> Event.objects.filter(start__contained_by=DateTimeTZRange(
    ...     timezone.now() - datetime.timedelta(hours=1),
    ...     timezone.now() + datetime.timedelta(hours=1),
    ... )
    <QuerySet [<Event: Soft play>]>

.. versionchanged:: 3.1

    Support for :class:`~django.db.models.SmallAutoField`,
    :class:`~django.db.models.AutoField`,
    :class:`~django.db.models.BigAutoField`,
    :class:`~django.db.models.SmallIntegerField`, and
    :class:`~django.db.models.DecimalField` was added.

.. fieldlookup:: rangefield.overlap

``overlap``
^^^^^^^^^^^

    >>> Event.objects.filter(ages__overlap=NumericRange(8, 12))
    <QuerySet [<Event: Soft play>]>

Comparison functions
~~~~~~~~~~~~~~~~~~~~

Range fields support the standard lookups: :lookup:`lt`, :lookup:`gt`,
:lookup:`lte` and :lookup:`gte`. These are not particularly helpful - they
compare the lower bounds first and then the upper bounds only if necessary.
This is also the strategy used to order by a range field. It is better to use
the specific range comparison operators.

.. fieldlookup:: rangefield.fully_lt

``fully_lt``
^^^^^^^^^^^^

The returned ranges are strictly less than the passed range. In other words,
all the points in the returned range are less than all those in the passed
range.

    >>> Event.objects.filter(ages__fully_lt=NumericRange(11, 15))
    <QuerySet [<Event: Soft play>]>

.. fieldlookup:: rangefield.fully_gt

``fully_gt``
^^^^^^^^^^^^

The returned ranges are strictly greater than the passed range. In other words,
the all the points in the returned range are greater than all those in the
passed range.

    >>> Event.objects.filter(ages__fully_gt=NumericRange(11, 15))
    <QuerySet [<Event: Pub trip>]>

.. fieldlookup:: rangefield.not_lt

``not_lt``
^^^^^^^^^^

The returned ranges do not contain any points less than the passed range, that
is the lower bound of the returned range is at least the lower bound of the
passed range.

    >>> Event.objects.filter(ages__not_lt=NumericRange(0, 15))
    <QuerySet [<Event: Soft play>, <Event: Pub trip>]>

.. fieldlookup:: rangefield.not_gt

``not_gt``
^^^^^^^^^^

The returned ranges do not contain any points greater than the passed range, that
is the upper bound of the returned range is at most the upper bound of the
passed range.

    >>> Event.objects.filter(ages__not_gt=NumericRange(3, 10))
    <QuerySet [<Event: Soft play>]>

.. fieldlookup:: rangefield.adjacent_to

``adjacent_to``
^^^^^^^^^^^^^^^

The returned ranges share a bound with the passed range.

    >>> Event.objects.filter(ages__adjacent_to=NumericRange(10, 21))
    <QuerySet [<Event: Soft play>, <Event: Pub trip>]>

Querying using the bounds
~~~~~~~~~~~~~~~~~~~~~~~~~

There are three transforms available for use in queries. You can extract the
lower or upper bound, or query based on emptiness.

.. fieldlookup:: rangefield.startswith

``startswith``
^^^^^^^^^^^^^^

Returned objects have the given lower bound. Can be chained to valid lookups
for the base field.

    >>> Event.objects.filter(ages__startswith=21)
    <QuerySet [<Event: Pub trip>]>

.. fieldlookup:: rangefield.endswith

``endswith``
^^^^^^^^^^^^

Returned objects have the given upper bound. Can be chained to valid lookups
for the base field.

    >>> Event.objects.filter(ages__endswith=10)
    <QuerySet [<Event: Soft play>]>

.. fieldlookup:: rangefield.isempty

``isempty``
^^^^^^^^^^^

Returned objects are empty ranges. Can be chained to valid lookups for a
:class:`~django.db.models.BooleanField`.

    >>> 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
-----------------------------

PostgreSQL allows the definition of custom range types. Django's model and form
field implementations use base classes below, and psycopg2 provides a
:func:`~psycopg2:psycopg2.extras.register_range` to allow use of custom range
types.

.. class:: RangeField(**options)

    Base class for model range fields.

    .. attribute:: base_field

        The model field class to use.

    .. attribute:: range_type

        The psycopg2 range type to use.

    .. attribute:: form_field

        The form field class to use. Should be a subclass of
        :class:`django.contrib.postgres.forms.BaseRangeField`.

.. class:: django.contrib.postgres.forms.BaseRangeField

    Base class for form range fields.

    .. attribute:: base_field

        The form field to use.

    .. attribute:: range_type

        The psycopg2 range type to use.

Range operators
---------------

.. 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 = '-|-'

RangeBoundary() expressions
---------------------------

.. class:: RangeBoundary(inclusive_lower=True, inclusive_upper=False)

    .. attribute:: inclusive_lower

        If ``True`` (default), the lower bound is inclusive ``'['``, otherwise
        it's exclusive ``'('``.

    .. attribute:: inclusive_upper

        If ``False`` (default), the upper bound is exclusive ``')'``, otherwise
        it's inclusive ``']'``.

A ``RangeBoundary()`` expression represents the range boundaries. It can be
used with a custom range functions that expected boundaries, for example to
define :class:`~django.contrib.postgres.constraints.ExclusionConstraint`. See
`the PostgreSQL documentation for the full details <https://www.postgresql.org/
docs/current/rangetypes.html#RANGETYPES-INCLUSIVITY>`_.