1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021 |
- ================================
- 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` and
- :class:`JSONField`.
- ``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``.
- Querying ``JSONField``
- ----------------------
- We will use the following example model::
- from django.contrib.postgres.fields import JSONField
- from django.db import models
- class Dog(models.Model):
- name = models.CharField(max_length=200)
- data = JSONField()
- def __str__(self):
- return self.name
- .. fieldlookup:: jsonfield.key
- Key, index, and path lookups
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- To query based on a given dictionary key, use that key as the lookup name::
- >>> Dog.objects.create(name='Rufus', data={
- ... 'breed': 'labrador',
- ... 'owner': {
- ... 'name': 'Bob',
- ... 'other_pets': [{
- ... 'name': 'Fishy',
- ... }],
- ... },
- ... })
- >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': None})
- >>> Dog.objects.filter(data__breed='collie')
- <QuerySet [<Dog: Meg>]>
- Multiple keys can be chained together to form a path lookup::
- >>> Dog.objects.filter(data__owner__name='Bob')
- <QuerySet [<Dog: Rufus>]>
- If the key is an integer, it will be interpreted as an index lookup in an
- array::
- >>> Dog.objects.filter(data__owner__other_pets__0__name='Fishy')
- <QuerySet [<Dog: Rufus>]>
- If the key you wish to query by clashes with the name of another lookup, use
- the :lookup:`jsonfield.contains` lookup instead.
- If only one key or index is used, the SQL operator ``->`` is used. If multiple
- operators are used then the ``#>`` operator is used.
- To query for ``null`` in JSON data, use ``None`` as a value::
- >>> Dog.objects.filter(data__owner=None)
- <QuerySet [<Dog: Meg>]>
- To query for missing keys, use the ``isnull`` lookup::
- >>> Dog.objects.create(name='Shep', data={'breed': 'collie'})
- >>> Dog.objects.filter(data__owner__isnull=True)
- <QuerySet [<Dog: Shep>]>
- .. warning::
- Since any string could be a key in a JSON object, 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.
- Containment and key operations
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- .. fieldlookup:: jsonfield.contains
- .. fieldlookup:: jsonfield.contained_by
- .. fieldlookup:: jsonfield.has_key
- .. fieldlookup:: jsonfield.has_any_keys
- .. fieldlookup:: jsonfield.has_keys
- :class:`~django.contrib.postgres.fields.JSONField` shares lookups relating to
- containment and keys with :class:`~django.contrib.postgres.fields.HStoreField`.
- - :lookup:`contains <hstorefield.contains>` (accepts any JSON rather than
- just a dictionary of strings)
- - :lookup:`contained_by <hstorefield.contained_by>` (accepts any JSON
- rather than just a dictionary of strings)
- - :lookup:`has_key <hstorefield.has_key>`
- - :lookup:`has_any_keys <hstorefield.has_any_keys>`
- - :lookup:`has_keys <hstorefield.has_keys>`
- .. _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, ``[)``.
- ``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
- ---------------
- .. 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 = '-|-'
- RangeBoundary() expressions
- ---------------------------
- .. versionadded:: 3.0
- .. 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>`_.
|