Campos de modelo específicos de PostgreSQL

Todos estos campos están disponibles desde el módulo django.contrib.postgres.fields.

Índices en estos campos

Index y Field.db_index ambos crean un índice B-tree, que no es particularmente útil al consultar tipos de datos complejos. Los índices como GinIndex y GistIndex son más adecuados, aunque la elección del índice depende de las consultas que estás utilizando. En general, GiST puede ser una buena opción para los campos de rango <range-fields> y HStoreField, y GIN puede resultar útil para ArrayField.

ArrayField

class ArrayField(base_field, size=None, **options)

Un campo para almacenar listas de datos. La mayoría de los tipos de campos pueden ser utilizados, y pasas otra instancia de campo como el base_field. También puedes especificar un size. ArrayField puede ser anidado para almacenar matrices multidimensionales.

Si le das al campo una default, asegúrate de que sea un llamable como list (para un valor por defecto vacío) o un llamable que devuelve una lista (como una función). Utilizar incorrectamente default=[] crea un valor por defecto mutable compartido entre todas las instancias de ArrayField.

base_field

Este es un argumento obligatorio.

Specifica el tipo de datos subyacente y comportamiento para la matriz. Debe ser una instancia de una clase que herede de Field. Por ejemplo, podría ser una IntegerField o una CharField. La mayoría de los tipos de campos están permitidos, con la excepción de aquellos que manejan datos relacionales (ForeignKey, OneToOneField y ManyToManyField) y campos de archivo (FileField y ImageField).

Es posible anidar campos de matriz - puedes especificar una instancia de ArrayField como el campo base. Por ejemplo:

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,
    )

La transformación de valores entre la base de datos y el modelo, la validación de los datos y la configuración, así como la serialización, se delegan en el campo base subyacente.

size

Este es un argumento opcional.

Si se pasa, la matriz tendrá un tamaño máximo tal como se especifica. Esto se pasará a la base de datos, aunque PostgreSQL no aplica la restricción actualmente.

Nota

Cuando anidas ArrayField, ya sea que utilices el parámetro size o no, PostgreSQL requiere que las matrices sean rectangulares:

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],
    ]
)

Si se requieren formas irregulares, entonces el campo subyacente debe hacerse nulo y los valores deben ser rellenados con None.

Consultar ArrayField

Hay un número de consultas personalizadas y transformaciones para ArrayField. Utilizaremos el siguiente modelo de ejemplo:

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

contiene

La traducción de los textos es la siguiente:

>>> 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

Esta es la inversa del lookup contains - los objetos devueltos serán aquellos donde los datos son un subconjunto de los valores pasados. Utiliza el operador SQL <@. Por ejemplo:

>>> 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

Devuelve objetos donde los datos comparten resultados con los valores pasados. Utiliza el operador SQL &&. Por ejemplo:

>>> Post.objects.create(name="First post", tags=["thoughts", "django"])
>>> Post.objects.create(name="Second post", tags=["thoughts", "tutorial"])
>>> 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>]>

>>> Post.objects.filter(tags__overlap=Post.objects.values_list("tags"))
<QuerySet [<Post: First post>, <Post: Second post>, <Post: Third post>]>

len

Devuelve la longitud del array. Los lookups disponibles después de esto son aquellos disponibles para IntegerField. Por ejemplo:

>>> 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>]>

Transformaciones de índice

Las transformaciones de índice convierten el índice en el array. Cualquier entero no negativo se puede utilizar. No hay errores si supera la longitud del array size. Los lookups disponibles después de la transformación son aquellos desde el campo base_field. Por ejemplo:

>>> 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 []>

Nota

PostgreSQL utiliza índices 1-basados para campos de arrays al escribir SQL crudo. Sin embargo, estos índices y los utilizados en slices usan índices 0-basados para ser consistentes con Python.

Slice transforms

Las transformaciones de slice toman una porción del array. Cualquier dos enteros no negativos pueden ser utilizados, separados por un solo guión bajo. Los lookups disponibles después de la transformación no cambian. Por ejemplo:

>>> 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>]>

Nota

PostgreSQL utiliza índices 1-basado para campos de arrays al escribir SQL crudo. Sin embargo, estas slices y las utilizadas en indexes utilizan índices 0-basados para ser consistentes con Python.

Arrays multidimensionales con índices y slices

PostgreSQL tiene un comportamiento bastante esotérico cuando se usan índices y slices en arrays multidimensionales. Siempre funcionará usar índices para llegar hasta la última información subyacente, pero la mayoría de las otras slices comportarse de manera extraña a nivel de base de datos y no pueden ser soportadas de manera lógica y consistente por Django.

HStoreField

class HStoreField(**options)

Un campo para almacenar pares clave-valor. El tipo de dato Python utilizado es un dict. Las claves deben ser cadenas, y los valores pueden ser cadenas o nulos (None en Python).

Para utilizar este campo, necesitarás:

  1. Agregar 'django.contrib.postgres' a tu INSTALLED_APPS.

  2. Configura la extensión hstore en 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.

Nota

En ocasiones puede ser útil requerir o restringir las claves válidas para un campo determinado. Esto se puede hacer utilizando la KeysValidator.

Consultar HStoreField

Además de la capacidad de consultar por clave, hay una serie de consultas personalizadas disponibles para HStoreField.

Usaremos el siguiente modelo de ejemplo:

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

Consultas por clave

Para consultar basándote en una clave determinada, puedes utilizar esa clave como nombre de consulta:

>>> 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>]>

Puedes encadenar otras consultas después de las consultas por clave:

>>> Dog.objects.filter(data__breed__contains="l")
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>

o utiliza expresiones F() para anotar el valor de una clave. Por ejemplo:

>>> from django.db.models import F
>>> rufus = Dog.objects.annotate(breed=F("data__breed"))[0]
>>> rufus.breed
'labrador'

Si la clave que deseas consultar coincide con el nombre de otra consulta, debes utilizar la hstorefield.contains consulta en su lugar.

Nota

Los textos traducidos son:

Advertencia

Dado que cualquier cadena podría ser una clave en un valor hstore, cualquier consulta de búsqueda distinta a las listadas a continuación se interpretará como una consulta de búsqueda por clave. No se levantan errores. Ten especial cuidado con los errores de tipado y siempre verifica que tus consultas funcionen como pretendes.

contiene

La consulta de búsqueda contains está sobrescrita en el campo HStoreField. Los objetos devueltos son aquellos donde las pares clave-valor dados están contenidos todos en el campo. Utiliza el operador SQL @>. Por ejemplo:

>>> 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

Esto es la inversa de la consulta de búsqueda contains - los objetos devueltos serán aquellos donde las pares clave-valor del objeto son un subconjunto de los en el valor pasado. Utiliza el operador SQL <@. Por ejemplo:

>>> 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

Devuelve objetos donde la clave dada está en los datos. Utiliza el operador SQL ?. Por ejemplo:

>>> 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

Devuelve objetos donde cualquiera de las claves dadas están en los datos. Utiliza el operador SQL ?|. Por ejemplo:

>>> 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

Devuelve objetos donde todas las claves dadas están en los datos. Utiliza el operador SQL ?&. Por ejemplo:

>>> 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

Devuelve objetos donde el array de claves es el valor dado. Ten en cuenta que el orden no está garantizado para ser confiable, por lo que esta transformación es principalmente útil para usar en conjunto con consultas de búsqueda sobre ArrayField. Utiliza la función SQL akeys(). Por ejemplo:

>>> 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

Devuelve objetos donde el array de valores es el valor dado. Ten en cuenta que el orden no está garantizado para ser confiable, por lo que esta transformación es principalmente útil para usar en conjunto con consultas de búsqueda sobre ArrayField. Utiliza la función SQL avals(). Por ejemplo:

>>> 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>]>

Campos de rango

Hay cinco tipos de campos de rango, correspondientes a los tipos de rango integrados en PostgreSQL. Estos campos se utilizan para almacenar un rango de valores; por ejemplo, los timestamps de inicio y fin de un evento, o el rango de edades que una actividad es adecuada.

Todos los campos de rango se traducen a objetos psycopg Range en Python, pero también aceptan tuplas como entrada si no se necesita información sobre límites. El valor por defecto es el límite inferior incluido y el límite superior excluido, es decir [) (consulte la documentación de PostgreSQL para detalles sobre diferentes límites). Los límites por defecto pueden cambiarse para campos de rango no discretos (DateTimeRangeField y DecimalRangeField) utilizando el argumento default_bounds.

PostgreSQL normaliza un rango con puntos vacíos al rango vacío

Un rango con valores iguales especificados para un límite inferior incluido y un límite superior excluido, como Range(datetime.date(2005, 6, 21), datetime.date(2005, 6, 21)) o [4, 4), no tiene puntos. PostgreSQL normalizará el valor al vacío cuando se guarde en la base de datos y los valores originales de límite se perderán. Consulte la documentación de PostgreSQL para detalles <https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-IO>`_.

IntegerRangeField

class IntegerRangeField(**options)

Almacena un rango de enteros. Basado en un IntegerField. Representado por un int4range en la base de datos y un django.db.backends.postgresql.psycopg_any.NumericRange en Python.

Independientemente de los límites especificados al guardar los datos, PostgreSQL siempre devuelve un rango en una forma canónica que incluye el límite inferior y excluye el límite superior, es decir [).

BigIntegerRangeField

class BigIntegerRangeField(**options)

Almacena un rango de enteros grandes. Basado en un BigIntegerField. Representado por un int8range en la base de datos y un django.db.backends.postgresql.psycopg_any.NumericRange en Python.

Independientemente de los límites especificados al guardar los datos, PostgreSQL siempre devuelve un rango en una forma canónica que incluye el límite inferior y excluye el límite superior, es decir [).

DecimalRangeField

class DecimalRangeField(default_bounds='[)', **options)

Almacena un rango de valores de punto flotante. Basado en un DecimalField. Representado por un numrange en la base de datos y un django.db.backends.postgresql.psycopg_any.NumericRange en Python.

default_bounds

Opcional. El valor de bounds para los inputs de lista y tupla. El valor predeterminado es el límite inferior incluido, el límite superior excluido, es decir [) (consulte la documentación de PostgreSQL para detalles sobre diferentes límites). No se utiliza default_bounds para los inputs django.db.backends.postgresql.psycopg_any.NumericRange.

DateTimeRangeField

class DateTimeRangeField(default_bounds='[)', **options)

Almacena un rango de timestamps. Basado en un DateTimeField. Representado por un tstzrange en la base de datos y un django.db.backends.postgresql.psycopg_any.DateTimeTZRange en Python.

default_bounds

Opcional. El valor de bounds para los inputs de lista y tupla. El valor predeterminado es el límite inferior incluido, el límite superior excluido, es decir [) (consulte la documentación de PostgreSQL para detalles sobre diferentes límites). No se utiliza default_bounds para los inputs django.db.backends.postgresql.psycopg_any.DateTimeTZRange.

Campo de Fecha Rango

class DateRangeField(**options)

Almacena un rango de fechas. Basado en un DateField. Representado por un daterange en la base de datos y un django.db.backends.postgresql.psycopg_any.DateRange en Python.

Independientemente de los límites especificados al guardar los datos, PostgreSQL siempre devuelve un rango en una forma canónica que incluye el límite inferior y excluye el límite superior, es decir [).

Consulta de Campos de Rango

Hay una serie de consultas personalizadas y transformaciones para campos de rango. Están disponibles en todos los campos anteriores, pero usaremos el siguiente modelo de ejemplo:

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

También utilizaremos los siguientes objetos de ejemplo:

>>> 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)
... )

y NumericRange:

>>> from django.db.backends.postgresql.psycopg_any import NumericRange

Funciones de contención

Al igual que otros campos PostgreSQL, hay tres operadores estándar de contención: contains, contained_by y overlap, utilizando los operadores SQL @>, <@, y && respectivamente.

contiene
>>> 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>]>

La función de búsqueda contained_by también está disponible en los tipos no de rango de campos: SmallAutoField, AutoField, BigAutoField, SmallIntegerField, IntegerField, BigIntegerField, DecimalField, FloatField, DateField, y DateTimeField. Por ejemplo:

>>> from django.db.backends.postgresql.psycopg_any 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>]>

Funciones de comparación

Los campos de rango admiten las consultas estándar: lt, gt, lte y gte. Estas no son particularmente útiles - comparan los límites inferiores primero y luego los superiores sólo si es necesario. Esta también es la estrategia utilizada para ordenar por un campo de rango. Es mejor utilizar los operadores específicos de comparación de rango.

fully_lt

Los rangos devueltos son estrictamente menores que el rango pasado. En otras palabras, todos los puntos en el rango devuelto son menores que todos aquellos en el rango pasado.

>>> Event.objects.filter(ages__fully_lt=NumericRange(11, 15))
<QuerySet [<Event: Soft play>]>
fully_gt

Los rangos devueltos son estrictamente mayores que el rango pasado. En otras palabras, todos los puntos en el rango devuelto son mayores que todos aquellos en el rango pasado.

>>> Event.objects.filter(ages__fully_gt=NumericRange(11, 15))
<QuerySet [<Event: Pub trip>]>
not_lt

Los rangos devueltos no contienen ningún punto menor que el rango pasado, es decir, el límite inferior del rango devuelto es al menos el límite inferior del rango pasado.

>>> Event.objects.filter(ages__not_lt=NumericRange(0, 15))
<QuerySet [<Event: Soft play>, <Event: Pub trip>]>
not_gt

Los rangos devueltos no contienen ningún punto mayor que el rango pasado, es decir, el límite superior del rango devuelto es como máximo el límite superior del rango pasado.

>>> Event.objects.filter(ages__not_gt=NumericRange(3, 10))
<QuerySet [<Event: Soft play>]>
adjacent_to

Los rangos devueltos comparten un límite con el rango pasado.

>>> Event.objects.filter(ages__adjacent_to=NumericRange(10, 21))
<QuerySet [<Event: Soft play>, <Event: Pub trip>]>

Consultar utilizando los límites

Los campos de rango admiten varias consultas adicionales.

startswith

Los objetos devueltos tienen el límite inferior dado. Pueden encadenarse a consultas válidas para el campo base.

>>> Event.objects.filter(ages__startswith=21)
<QuerySet [<Event: Pub trip>]>
endswith

Los objetos devueltos tienen el límite superior dado. Pueden encadenarse a consultas válidas para el campo base.

>>> Event.objects.filter(ages__endswith=10)
<QuerySet [<Event: Soft play>]>
isempty

Los objetos devueltos son rangos vacíos. Pueden encadenarse a consultas válidas para un BooleanField.

>>> Event.objects.filter(ages__isempty=True)
<QuerySet []>
lower_inc

Devuelve objetos que tienen límites inferiores inclusivos o excluyentes, dependiendo del valor booleano pasado. Pueden encadenarse a consultas válidas para un BooleanField.

>>> Event.objects.filter(ages__lower_inc=True)
<QuerySet [<Event: Soft play>, <Event: Pub trip>]>
lower_inf

Devuelve objetos que tienen un límite inferior ilimitado (infinito) o limitado, dependiendo del valor booleano pasado. Puede ser encadenada a consultas válidas para un BooleanField.

>>> Event.objects.filter(ages__lower_inf=True)
<QuerySet []>
upper_inc

Devuelve objetos que tienen límites superiores inclusivos o excluyentes, dependiendo del valor booleano pasado. Puede ser encadenada a consultas válidas para un BooleanField.

>>> Event.objects.filter(ages__upper_inc=True)
<QuerySet []>
upper_inf

Devuelve objetos que tienen un límite superior ilimitado (infinito) o limitado, dependiendo del valor booleano pasado. Puede ser encadenada a consultas válidas para un BooleanField.

>>> Event.objects.filter(ages__upper_inf=True)
<QuerySet [<Event: Pub trip>]>

Definiendo tus propios tipos de rango

PostgreSQL permite la definición de tipos de rango personalizados. Las implementaciones de modelos y campos de formulario de Django utilizan clases base a continuación, y psycopg proporciona una register_range() para permitir el uso de tipos de rango personalizados.

class RangeField(**options)

Clase base para campos de modelo de rango.

base_field

La clase del campo de modelo a utilizar.

range_type

El tipo de rango a utilizar.

form_field

La clase del campo de formulario a utilizar. Debe ser una subclase de django.contrib.postgres.forms.BaseRangeField.

class django.contrib.postgres.forms.BaseRangeField

Clase base para campos de formulario de rango.

base_field

El campo de formulario a utilizar.

range_type

El tipo de rango a utilizar.

Operadores de rango

class RangeOperators

PostgreSQL proporciona un conjunto de operadores SQL que se pueden usar junto con los tipos de datos de rango (consulte la documentación de PostgreSQL para obtener detalles completos sobre los operadores de rango). Esta clase está diseñada como un método conveniente para evitar errores de escritura. Los nombres de los operadores se superponen con los nombres de las correspondientes consultas.

class RangeOperators:
    EQUAL = "="
    NOT_EQUAL = "<>"
    CONTAINS = "@>"
    CONTAINED_BY = "<@"
    OVERLAPS = "&&"
    FULLY_LT = "<<"
    FULLY_GT = ">>"
    NOT_LT = "&>"
    NOT_GT = "&<"
    ADJACENT_TO = "-|-"

Expresiones RangeBoundary()

class RangeBoundary(inclusive_lower=True, inclusive_upper=False)
inclusive_lower

Si True (por defecto), la frontera inferior es inclusiva '[', en caso contrario es exclusiva '('.

inclusive_upper

Si False (por defecto), la frontera superior es exclusiva ')', en caso contrario es inclusiva ']'.

Una expresión RangeBoundary() representa las fronteras del rango. Puede usarse con funciones de rango personalizadas que esperan fronteras, por ejemplo para definir ExclusionConstraint. Consulte la documentación de PostgreSQL para obtener detalles completos.