Поля модели, специфичные для PostgreSQL
Все эти поля доступны из модуля django.contrib.postgres.fields.
Индексация этих полей
Index и Field.db_index оба создают индекс B-дерева, который не особенно полезен при запросе сложных типов данных. Индексы, такие как GinIndex и GistIndex, более подходят, хотя выбор индекса зависит от используемых запросов. Как правило, GiST может быть хорошим выбором для полей диапазона и HStoreField, а GIN может быть полезен для ArrayField и JSONField.
ArrayField
-
class ArrayField(base_field, size=None, **options)[source] -
Поле для хранения списков данных. Большинство типов полей могут быть использованы, просто передайте другой экземпляр поля в качестве
base_field. Вы также можете указатьsize.ArrayFieldможет быть вложен для хранения многомерных массивов.Если вы задаёте полю
default, убедитесь, что это вызываемый объект, такой какlist(для пустого значения по умолчанию) или вызываемый объект, возвращающий список (например, функция). Неправильное использованиеdefault=[]создаёт изменяемое значение по умолчанию, которое используется всеми экземплярамиArrayField.-
base_field -
Это обязательный аргумент.
Указывает базовый тип данных и поведение для массива. Это должен быть экземпляр подкласса
Field. Например, это может бытьIntegerFieldилиCharField. Допускается большинство типов полей, за исключением тех, которые обрабатывают реляционные данные (ForeignKey,OneToOneFieldиManyToManyField).Возможна вложенность полей массивов — можно указать экземпляр
ArrayFieldв качествеbase_field. Например: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, )Преобразование значений между базой данных и моделью, проверка данных и конфигурация, а также сериализация делегируются базовому полю.
-
size -
Это необязательный аргумент.
Если передано, массив будет иметь максимальный размер, как указано. Это будет передано в базу данных, хотя PostgreSQL в настоящее время не обеспечивает ограничение.
-
Примечание
При вложении ArrayField, независимо от использования параметра size, PostgreSQL требует, чтобы массивы были прямоугольными:
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],
])
Если требуются нерегулярные формы, то базовое поле должно быть сделано nullable, а значения заполнены None.
Запрос ArrayField
Существует ряд пользовательских поисковых запросов и преобразований для ArrayField. Мы воспользуемся следующей примерной моделью:
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
contains
Поиск contains переопределен для ArrayField. Возвращаемые объекты будут теми, где переданные значения являются подмножеством данных. Используется оператор SQL @>. Например:
>>> 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>]>
contained_by
Это обратное для поиска contains — возвращаемые объекты будут теми, где данные являются подмножеством переданных значений. Используется оператор SQL <@. Например:
>>> 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>]>
overlap
Возвращает объекты, где данные имеют общие результаты со значениями, переданными. Используется оператор SQL &&. Например:
>>> 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>]>
len
Возвращает длину массива. Доступные поисковые запросы после этого — те, которые доступны для IntegerField. Например:
>>> 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>]>
Преобразования индекса
Преобразования индекса индексируют в массив. Можно использовать любые неотрицательные целые числа. Нет ошибок, если он превышает size массива. Доступные поисковые запросы после преобразования — те, что доступны для base_field. Например:
>>> 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 []>
Примечание
PostgreSQL использует индексирование с 1 для полей массива при записи SQL-кода. Однако эти индексы и те, которые используются в slices, используют индексирование с 0 для согласованности с Python.
Преобразования срезов
Преобразования срезов берут срез массива. Можно использовать любые два неотрицательных целых числа, разделённых единственным символом подчёркивания. Доступные поисковые запросы после преобразования не меняются. Например:
>>> 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>]>
Примечание
PostgreSQL использует индексирование с 1 для полей массива при записи SQL-кода. Однако эти срезы и те, которые используются в indexes, используют индексирование с 0 для согласованности с Python.
Многомерные массивы с индексами и срезами
PostgreSQL имеет довольно специфичное поведение при использовании индексов и срезов многомерных массивов. Всегда можно использовать индексы для доступа к конечным данным, но большинство других срезов ведут себя странно на уровне базы данных и не могут быть поддержаны в логичном, согласованном виде Django.
CIText поля
-
class CIText(**options)[source] -
Добавлена в Django 1.11.
Миксин для создания полей текста с регистронезависимым сравнением, поддерживаемых типом citext. Прочитайте о соображениях по производительности, прежде чем использовать его.
Для использования
citext, используйте операциюCITextExtensionдля настройки расширения citext в PostgreSQL перед первой операцией миграцииCreateModel.Предоставлено несколько полей, использующих миксин:
-
class CICharField(**options)[source]
-
class CIEmailField(**options)[source]
-
class CITextField(**options)[source] -
Эти поля являются подклассами
CharField,EmailFieldиTextFieldсоответственно.Эти ограничения не будут применяться в базе данных, так как
citextведет себя аналогично типуtextв PostgreSQL.
HStoreField
-
class HStoreField(**options)[source] -
Поле для хранения пар ключ-значение. Используемый в Python тип данных —
dict. Ключи должны быть строками, а значения могут быть либо строками, либо значениями NULL (Noneв Python).Для использования этого поля вам нужно:
- Добавить
'django.contrib.postgres'в свойINSTALLED_APPS. - Настроить расширение hstore в PostgreSQL.
Вы увидите ошибку, подобную
can't adapt type 'dict', если вы пропустите первый шаг, илиtype "hstore" does not exist, если вы пропустите второй.Изменено в Django 1.11:Добавлена возможность хранить значения NULL. Ранее они преобразовывались в строки.
- Добавить
Примечание
В некоторых случаях может быть полезно потребовать или ограничить допустимые ключи для данного поля. Это можно сделать с помощью KeysValidator.
Запросы к полю HStoreField
Помимо возможности запроса по ключу, для HStoreField доступно несколько пользовательских типов поиска.
Мы будем использовать следующую модель в качестве примера:
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
Поиск по ключу
Для запроса по заданному ключу просто используйте этот ключ в качестве имени поиска:
>>> 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>]>
Вы можете комбинировать другие типы поиска после поиска по ключу:
>>> Dog.objects.filter(data__breed__contains='l') <QuerySet [<Dog: Rufus>, <Dog: Meg>]>
Если ключ, по которому вы хотите выполнить поиск, совпадает с именем другого типа поиска, используйте вместо него тип поиска hstorefield.contains.
Предупреждение
Поскольку любой строковый ключ может быть ключом значения hstore, любой тип поиска, кроме перечисленных ниже, будет интерпретирован как поиск по ключу. Ошибки не генерируются. Будьте внимательны к ошибкам ввода и всегда проверяйте, что ваши запросы работают так, как вы ожидаете.
contains
Тип поиска contains переопределён для HStoreField. Возвращаемые объекты — те, где все заданные пары ключ-значение содержатся в поле. Используется оператор SQL @>. Например:
>>> 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>]>
contained_by
Это обратный тип поиска contains — возвращаемые объекты будут теми, где пары ключ-значение объекта являются подмножеством тех, что переданы в значение. Используется оператор SQL <@. Например:
>>> 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>]>
has_key
Возвращает объекты, где указанный ключ находится в данных. Использует оператор SQL ?. Например:
>>> 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>]>
has_any_keys
Возвращает объекты, где любой из заданных ключей находится в данных. Использует оператор SQL ?|. Например:
>>> 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>]>
has_keys
Возвращает объекты, где все заданные ключи находятся в данных. Использует оператор SQL ?&. Например:
>>> 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>]>
keys
Возвращает объекты, где массив ключей является заданным значением. Обратите внимание, что порядок не гарантирован, поэтому этот преобразователь в основном полезен для использования в сочетании с типами поиска для ArrayField. Использует функцию SQL akeys(). Например:
>>> 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>]>
values
Возвращает объекты, где массив значений является заданным значением. Обратите внимание, что порядок не гарантирован, поэтому этот преобразователь в основном полезен для использования в сочетании с типами поиска для ArrayField. Использует функцию SQL avalues(). Например:
>>> 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)[source] -
Поле для хранения данных в формате JSON. В Python данные представлены в своём родном формате: словари, списки, строки, числа, булевы значения и
None.-
encoder -
Новое в Django 1.11.
Дополнительный класс кодирования JSON для сериализации типов данных, не поддерживаемых стандартным сериализатором JSON (
datetime,uuid, и т. д.). Например, можно использовать классDjangoJSONEncoderили любой другой подклассjson.JSONEncoder.При получении значения из базы данных оно будет в формате, выбранном пользовательским кодером (чаще всего строка), поэтому вам нужно будет предпринять дополнительные шаги, чтобы преобразовать значение обратно в исходный тип данных (
Model.from_db()иField.from_db_value()— два возможных крючка для этой цели). Ваша десериализация может потребовать учитывать тот факт, что вы не можете быть уверены в типе ввода. Например, вы рискуете получитьdatetime, который на самом деле был строкой, которая просто оказалась в том же формате, который был выбран дляdatetime.
Если вы задаёте для поля
default, убедитесь, что это вызываемый объект, такой какdict(для пустого значения по умолчанию) или вызываемый объект, возвращающий словарь (например, функция). Неправильное использованиеdefault={}создаёт изменяемое значение по умолчанию, которое используется всеми экземплярамиJSONField. -
Примечание
PostgreSQL имеет два встроенных типа данных на основе JSON: json и jsonb. Основное различие между ними заключается в том, как они хранятся и как к ним можно обратиться. Поле PostgreSQL json хранится в виде исходного строкового представления JSON и должно декодироваться на лету при запросе по ключам. Поле jsonb хранится в соответствии с фактической структурой JSON, что позволяет индексировать. Недостатком является небольшая дополнительная стоимость при записи в поле jsonb. JSONField использует jsonb.
В результате, это поле требует PostgreSQL ≥ 9.4.
Запросы к полю JSONField
Мы будем использовать следующую модель в качестве примера:
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
Поиск по ключу, индексу и пути
Для поиска по заданному ключу словаря просто используйте этот ключ в качестве имени поиска:
>>> Dog.objects.create(name='Rufus', data={
... 'breed': 'labrador',
... 'owner': {
... 'name': 'Bob',
... 'other_pets': [{
... 'name': 'Fishy',
... }],
... },
... })
>>> Dog.objects.create(name='Meg', data={'breed': 'collie'})
>>> Dog.objects.filter(data__breed='collie')
<QuerySet [<Dog: Meg>]>
Несколько ключей могут быть объединены для поиска по пути:
>>> Dog.objects.filter(data__owner__name='Bob') <QuerySet [<Dog: Rufus>]>
Если ключ является целым числом, он будет интерпретироваться как поиск по индексу в массиве:
>>> Dog.objects.filter(data__owner__other_pets__0__name='Fishy') <QuerySet [<Dog: Rufus>]>
Если ключ, по которому вы хотите выполнить поиск, совпадает с именем другого типа поиска, используйте вместо него тип поиска jsonfield.contains.
Если используется только один ключ или индекс, используется оператор SQL ->. Если используются несколько операторов, то используется оператор #>.
Предупреждение
Поскольку любой строковый ключ может быть ключом в объекте JSON, любой тип поиска, кроме перечисленных ниже, будет интерпретироваться как поиск по ключу. Ошибки не генерируются. Будьте внимательны к ошибкам ввода и всегда проверяйте, что ваши запросы работают так, как вы ожидаете.
Операции с содержанием и ключами
JSONField использует типы поиска, относящиеся к содержанию и ключам, как и HStoreField.
-
contains(принимает любой JSON, а не только словарь строк) -
contained_by(принимает любой JSON, а не только словарь строк) has_keyhas_any_keyshas_keys
Поля диапазонов
Существует пять типов полей диапазонов, соответствующих встроенным типам диапазонов в PostgreSQL. Эти поля используются для хранения диапазона значений; например, начальная и конечная временные метки события или диапазон возрастов, для которых подходит определённая активность.
Все поля диапазонов преобразуются в объекты psycopg2 Range в Python, но также принимают кортежи в качестве входных данных, если информация о границах не требуется. По умолчанию включена нижняя граница, исключена верхняя граница; то есть [).
IntegerRangeField
-
class IntegerRangeField(**options)[source] -
Хранит диапазон целых чисел. Основан на
IntegerField. Представлен какint4rangeв базе данных и какNumericRangeв Python.Независимо от указанных границ при сохранении данных, PostgreSQL всегда возвращает диапазон в канонической форме, включающей нижнюю границу и исключающей верхнюю границу; то есть
[).
BigIntegerRangeField
-
class BigIntegerRangeField(**options)[source] -
Хранит диапазон больших целых чисел. Основан на
BigIntegerField. Представлен какint8rangeв базе данных и какNumericRangeв Python.Независимо от указанных границ при сохранении данных, PostgreSQL всегда возвращает диапазон в канонической форме, включающей нижнюю границу и исключающей верхнюю границу; то есть
[).
FloatRangeField
-
class FloatRangeField(**options)[source] -
Хранит диапазон значений с плавающей точкой. Основан на
FloatField. Представлен какnumrangeв базе данных и какNumericRangeв Python.
DateTimeRangeField
-
class DateTimeRangeField(**options)[source] -
Хранит диапазон временных меток. Основан на
DateTimeField. Представлен какtstzrangeв базе данных и какDateTimeTZRangeв Python.
DateRangeField
-
class DateRangeField(**options)[source] -
Хранит диапазон дат. Основан на
DateField. Представлен какdaterangeв базе данных и какDateRangeв Python.Независимо от указанных границ при сохранении данных, PostgreSQL всегда возвращает диапазон в канонической форме, включающей нижнюю границу и исключающей верхнюю границу; то есть
[).
Запросы к полям диапазонов
Существует ряд пользовательских запросов и преобразований для полей диапазонов. Они доступны для всех вышеперечисленных полей, но мы будем использовать следующую примерную модель:
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
Мы также будем использовать следующие примерные объекты:
>>> 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))
и NumericRange:
>>> from psycopg2.extras import NumericRange
Функции включения
Как и для других полей PostgreSQL, существуют три стандартных оператора включения: contains, contained_by и overlap, использующие операторы SQL @>, <@, и && соответственно.
contains
>>> Event.objects.filter(ages__contains=NumericRange(4, 5)) <QuerySet [<Event: Soft play>]>
contained_by
>>> Event.objects.filter(ages__contained_by=NumericRange(0, 15)) <QuerySet [<Event: Soft play>]>
Запрос contained_by также доступен для типов полей без диапазона: IntegerField, BigIntegerField, FloatField, DateField и DateTimeField. Например:
>>> 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>]>
overlap
>>> Event.objects.filter(ages__overlap=NumericRange(8, 12)) <QuerySet [<Event: Soft play>]>
Функции сравнения
Поля диапазонов поддерживают стандартные запросы: lt, gt, lte и gte. Они не особенно полезны — они сравнивают сначала нижние границы, а затем верхние, только если это необходимо. Это также стратегия, используемая для сортировки по полю диапазона. Лучше использовать конкретные операторы сравнения диапазонов.
fully_lt
Возвращаемые диапазоны строго меньше переданного диапазона. Другими словами, все точки в возвращаемом диапазоне меньше всех точек в переданном диапазоне.
>>> Event.objects.filter(ages__fully_lt=NumericRange(11, 15)) <QuerySet [<Event: Soft play>]>
fully_gt
Возвращаемые диапазоны строго больше переданного диапазона. Другими словами, все точки в возвращаемом диапазоне больше всех точек в переданном диапазоне.
>>> Event.objects.filter(ages__fully_gt=NumericRange(11, 15)) <QuerySet [<Event: Pub trip>]>
not_lt
Возвращаемые диапазоны не содержат точек, меньших переданного диапазона, то есть нижняя граница возвращаемого диапазона больше или равна нижней границе переданного диапазона.
>>> Event.objects.filter(ages__not_lt=NumericRange(0, 15)) <QuerySet [<Event: Soft play>, <Event: Pub trip>]>
not_gt
Возвращаемые диапазоны не содержат точек, больших переданного диапазона, то есть верхняя граница возвращаемого диапазона меньше или равна верхней границе переданного диапазона.
>>> Event.objects.filter(ages__not_gt=NumericRange(3, 10)) <QuerySet [<Event: Soft play>]>
adjacent_to
Возвращаемые диапазоны разделяют границу с переданным диапазоном.
>>> Event.objects.filter(ages__adjacent_to=NumericRange(10, 21)) <QuerySet [<Event: Soft play>, <Event: Pub trip>]>
Запросы с использованием границ
Доступны три преобразования для использования в запросах. Вы можете извлечь нижнюю или верхнюю границу или выполнить запрос на основе пустоты.
startswith
Объекты с заданной нижней границей. Можно использовать цепочку с валидными запросами для базового поля.
>>> Event.objects.filter(ages__startswith=21) <QuerySet [<Event: Pub trip>]>
endswith
Объекты с заданной верхней границей. Можно использовать цепочку с валидными запросами для базового поля.
>>> Event.objects.filter(ages__endswith=10) <QuerySet [<Event: Soft play>]>
isempty
Возвращаемые объекты — пустые диапазоны. Можно использовать цепочку с валидными запросами для BooleanField.
>>> Event.objects.filter(ages__isempty=True) <QuerySet []>
Определение собственных типов диапазонов
PostgreSQL позволяет определять пользовательские типы диапазонов. Реализации полей модели и формы Django используют базовые классы ниже, а psycopg2 предоставляет register_range() для использования пользовательских типов диапазонов.
-
class RangeField(**options)[source] -
Базовый класс для полей диапазона модели.
-
base_field -
Класс поля модели для использования.
-
range_type -
Тип диапазона psycopg2 для использования.
-
form_field -
Класс поля формы для использования. Должен быть подклассом
django.contrib.postgres.forms.BaseRangeField.
-
-
class django.contrib.postgres.forms.BaseRangeField -
Базовый класс для полей диапазона формы.
-
base_field -
Поле формы для использования.
-
range_type -
Тип диапазона psycopg2 для использования.
-
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.0/ref/contrib/postgres/fields/