123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- PostgreSQL specific model fields
- ================================
- All of these fields are available from the ``django.contrib.postgres.fields``
- module.
- .. currentmodule:: django.contrib.postgres.fields
- ArrayField
- ----------
- .. class:: ArrayField(base_field, size=None, **options)
- A field for storing lists of data. Most field types can be used, you simply
- 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.
- .. attribute:: base_field
- This is a required argument.
- Specifies the underlying data type and behaviour 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.db import models
- from django.contrib.postgres.fields import ArrayField
- class ChessBoard(models.Model):
- board = ArrayField(
- ArrayField(
- CharField(max_length=10, blank=True, null=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.db import models
- from django.contrib.postgres.fields import ArrayField
- 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.db import models
- from django.contrib.postgres.fields import ArrayField
- class Post(models.Model):
- name = models.CharField(max_length=200)
- tags = ArrayField(models.CharField(max_length=200), blank=True)
- def __str__(self): # __unicode__ on python 2
- 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'])
- [<Post: First post>, <Post: Second post>]
- >>> Post.objects.filter(tags__contains=['django'])
- [<Post: First post>, <Post: Third post>]
- >>> Post.objects.filter(tags__contains=['django', 'thoughts'])
- [<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'])
- [<Post: First post>]
- >>> Post.objects.filter(tags__contained_by=['thoughts', 'django', 'tutorial'])
- [<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'])
- [<Post: First post>, <Post: Second post>]
- >>> Post.objects.filter(tags__overlap=['thoughts', 'tutorial'])
- [<Post: First post>, <Post: Second post>, <Post: Third post>]
- .. fieldlookup:: arrayfield.index
- Index transforms
- ~~~~~~~~~~~~~~~~
- This class of transforms allows you to index into the array in queries. 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')
- [<Post: First post>, <Post: Second post>]
- >>> Post.objects.filter(tags__1__iexact='Django')
- [<Post: First post>]
- >>> Post.objects.filter(tags__276='javascript')
- []
- .. 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
- ~~~~~~~~~~~~~~~~
- This class of transforms allow you to 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'])
- [<Post: First post>]
- >>> Post.objects.filter(tags__0_2__contains='thoughts')
- [<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 behaviour 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.
- Indexing ArrayField
- ^^^^^^^^^^^^^^^^^^^
- At present using :attr:`~django.db.models.Field.db_index` will create a
- ``btree`` index. This does not offer particularly significant help to querying.
- A more useful index is a ``GIN`` index, which you should create using a
- :class:`~django.db.migrations.operations.RunSQL` operation.
|