summaryrefslogtreecommitdiff
path: root/docs/ref/contrib/postgres
diff options
context:
space:
mode:
Diffstat (limited to 'docs/ref/contrib/postgres')
-rw-r--r--docs/ref/contrib/postgres/fields.txt228
-rw-r--r--docs/ref/contrib/postgres/forms.txt135
-rw-r--r--docs/ref/contrib/postgres/index.txt28
3 files changed, 391 insertions, 0 deletions
diff --git a/docs/ref/contrib/postgres/fields.txt b/docs/ref/contrib/postgres/fields.txt
new file mode 100644
index 0000000000..dcde84d2ec
--- /dev/null
+++ b/docs/ref/contrib/postgres/fields.txt
@@ -0,0 +1,228 @@
+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.
diff --git a/docs/ref/contrib/postgres/forms.txt b/docs/ref/contrib/postgres/forms.txt
new file mode 100644
index 0000000000..6cad537f3b
--- /dev/null
+++ b/docs/ref/contrib/postgres/forms.txt
@@ -0,0 +1,135 @@
+PostgreSQL specific form fields and widgets
+===========================================
+
+All of these fields and widgets are available from the
+``django.contrib.postgres.forms`` module.
+
+.. currentmodule:: django.contrib.postgres.forms
+
+SimpleArrayField
+----------------
+
+.. class:: SimpleArrayField(base_field, delimiter=',', max_length=None, min_length=None)
+
+ A simple field which maps to an array. It is represented by an HTML
+ ``<input>``.
+
+ .. attribute:: base_field
+
+ This is a required argument.
+
+ It specifies the underlying form field for the array. This is not used
+ to render any HTML, but it is used to process the submitted data and
+ validate it. For example::
+
+ >>> from django.contrib.postgres.forms import SimpleArrayField
+ >>> from django import forms
+
+ >>> class NumberListForm(forms.Form):
+ ... numbers = SimpleArrayField(forms.IntegerField())
+
+ >>> form = NumberListForm({'numbers': '1,2,3'})
+ >>> form.is_valid()
+ True
+ >>> form.cleaned_data
+ {'numbers': [1, 2, 3]}
+
+ >>> form = NumberListForm({'numbers': '1,2,a'})
+ >>> form.is_valid()
+ False
+
+ .. attribute:: delimiter
+
+ This is an optional argument which defaults to a comma: ``,``. This
+ value is used to split the submitted data. It allows you to chain
+ ``SimpleArrayField`` for multidimensional data::
+
+ >>> from django.contrib.postgres.forms import SimpleArrayField
+ >>> from django import forms
+
+ >>> class GridForm(forms.Form):
+ ... places = SimpleArrayField(SimpleArrayField(IntegerField()), delimiter='|')
+
+ >>> form = GridForm({'places': '1,2|2,1|4,3'})
+ >>> form.is_valid()
+ True
+ >>> form.cleaned_data
+ {'places': [[1, 2], [2, 1], [4, 3]]}
+
+ .. note::
+
+ The field does not support escaping of the delimiter, so be careful
+ in cases where the delimiter is a valid character in the underlying
+ field. The delimiter does not need to be only one character.
+
+ .. attribute:: max_length
+
+ This is an optional argument which validates that the array does not
+ exceed the stated length.
+
+ .. attribute:: min_length
+
+ This is an optional argument which validates that the array reaches at
+ least the stated length.
+
+ .. admonition:: User friendly forms
+
+ ``SimpleArrayField`` is not particularly user friendly in most cases,
+ however it is a useful way to format data from a client-side widget for
+ submission to the server.
+
+SplitArrayField
+---------------
+
+.. class:: SplitArrayField(base_field, size, remove_trailing_nulls=False)
+
+ This field handles arrays by reproducing the underlying field a fixed
+ number of times.
+
+ .. attribute:: base_field
+
+ This is a required argument. It specifies the form field to be
+ repeated.
+
+ .. attribute:: size
+
+ This is the fixed number of times the underlying field will be used.
+
+ .. attribute:: remove_trailing_nulls
+
+ By default, this is set to ``False``. When ``False``, each value from
+ the repeated fields is stored. When set to ``True``, any trailing
+ values which are blank will be stripped from the result. If the
+ underlying field has ``required=True``, but ``remove_trailing_nulls``
+ is ``True``, then null values are only allowed at the end, and will be
+ stripped.
+
+ Some examples::
+
+ SplitArrayField(IntegerField(required=True), size=3, remove_trailing_nulls=False)
+
+ ['1', '2', '3'] # -> [1, 2, 3]
+ ['1', '2', ''] # -> ValidationError - third entry required.
+ ['1', '', '3'] # -> ValidationError - second entry required.
+ ['', '2', ''] # -> ValidationError - first and third entries required.
+
+ SplitArrayField(IntegerField(required=False), size=3, remove_trailing_nulls=False)
+
+ ['1', '2', '3'] # -> [1, 2, 3]
+ ['1', '2', ''] # -> [1, 2, None]
+ ['1', '', '3'] # -> [1, None, 3]
+ ['', '2', ''] # -> [None, 2, None]
+
+ SplitArrayField(IntegerField(required=True), size=3, remove_trailing_nulls=True)
+
+ ['1', '2', '3'] # -> [1, 2, 3]
+ ['1', '2', ''] # -> [1, 2]
+ ['1', '', '3'] # -> ValidationError - second entry required.
+ ['', '2', ''] # -> ValidationError - first entry required.
+
+ SplitArrayField(IntegerField(required=False), size=3, remove_trailing_nulls=True)
+
+ ['1', '2', '3'] # -> [1, 2, 3]
+ ['1', '2', ''] # -> [1, 2]
+ ['1', '', '3'] # -> [1, None, 3]
+ ['', '2', ''] # -> [None, 2]
diff --git a/docs/ref/contrib/postgres/index.txt b/docs/ref/contrib/postgres/index.txt
new file mode 100644
index 0000000000..5db4ab80ed
--- /dev/null
+++ b/docs/ref/contrib/postgres/index.txt
@@ -0,0 +1,28 @@
+``django.contrib.postgres``
+===========================
+
+PostgreSQL has a number of features which are not shared by the other databases
+Django supports. This optional module contains model fields and form fields for
+a number of PostgreSQL specific data types.
+
+.. note::
+ Django is, and will continue to be, a database-agnostic web framework. We
+ would encourage those writing reusable applications for the Django
+ community to write database-agnostic code where practical. However, we
+ recognise that real world projects written using Django need not be
+ database-agnostic. In fact, once a project reaches a given size changing
+ the underlying data store is already a significant challenge and is likely
+ to require changing the code base in some ways to handle differences
+ between the data stores.
+
+ Django provides support for a number of data types which will
+ only work with PostgreSQL. There is no fundamental reason why (for example)
+ a ``contrib.mysql`` module does not exist, except that PostgreSQL has the
+ richest feature set of the supported databases so its users have the most
+ to gain.
+
+.. toctree::
+ :maxdepth: 2
+
+ fields
+ forms