2014-03-27 00:44:21 +08:00
|
|
|
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.
|
|
|
|
|
2014-05-28 07:46:48 +08:00
|
|
|
Specifies the underlying data type and behavior for the array. It
|
2014-03-27 00:44:21 +08:00
|
|
|
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.contrib.postgres.fields import ArrayField
|
2014-03-15 01:34:49 +08:00
|
|
|
from django.db import models
|
2014-03-27 00:44:21 +08:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2014-03-15 01:34:49 +08:00
|
|
|
def __str__(self): # __unicode__ on Python 2
|
2014-03-27 00:44:21 +08:00
|
|
|
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'])
|
2014-05-26 21:44:50 +08:00
|
|
|
[<Post: First post>, <Post: Second post>]
|
2014-03-27 00:44:21 +08:00
|
|
|
|
|
|
|
>>> 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>]
|
|
|
|
|
2014-05-22 20:42:31 +08:00
|
|
|
.. 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)
|
|
|
|
[<Post: Second post>]
|
|
|
|
|
2014-03-27 00:44:21 +08:00
|
|
|
.. 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
|
|
|
|
|
2014-05-28 07:46:48 +08:00
|
|
|
PostgreSQL has some rather esoteric behavior when using indexes and slices
|
2014-03-27 00:44:21 +08:00
|
|
|
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.
|
2014-03-15 01:34:49 +08:00
|
|
|
|
|
|
|
HStoreField
|
|
|
|
-----------
|
|
|
|
|
|
|
|
.. class:: HStoreField(**options)
|
|
|
|
|
|
|
|
A field for storing mappings of strings to strings. The Python data type
|
|
|
|
used is a ``dict``.
|
|
|
|
|
|
|
|
.. 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): # __unicode__ on Python 2
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
.. fieldlookup:: hstorefield.key
|
|
|
|
|
|
|
|
Key lookups
|
|
|
|
~~~~~~~~~~~
|
|
|
|
|
|
|
|
To query based on a given key, you simply 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')
|
|
|
|
[<Dog: Meg>]
|
|
|
|
|
|
|
|
You can chain other lookups after key lookups::
|
|
|
|
|
|
|
|
>>> Dog.objects.filter(data__breed__contains='l')
|
|
|
|
[<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'})
|
|
|
|
[<Dog: Rufus>, <Dog: Meg>]
|
|
|
|
|
|
|
|
>>> Dog.objects.filter(data__contains={'breed': 'collie'})
|
|
|
|
[<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'})
|
|
|
|
[<Dog: Meg>, <Dog: Fred>]
|
|
|
|
|
|
|
|
>>> Dog.objects.filter(data__contained_by={'breed': 'collie'})
|
|
|
|
[<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')
|
|
|
|
[<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'])
|
|
|
|
[<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'])
|
|
|
|
[<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'])
|
|
|
|
[<Dog: Meg>]
|