• en
  • Language: fr

Champs de modèles spécifiques à PostgreSQL

Tous ces champs sont disponibles dans le module django.contrib.postgres.fields.

ArrayField

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

Un champ pour stocker des listes de données. La plupart des types de champs peuvent être utilisés ; il suffit de passer une autre instance de champ dans l’attribut base_field. Vous pouvez aussi indiquer une taille dans size. ArrayField peut être imbriqué pour stocker des tableaux multidimensionnels.

Si vous donnez une valeur default à ce champ, assurez-vous que ce soit un objet exécutable tel que list (pour une valeur par défaut vide) ou un objet exécutable qui renvoie une liste (par exemple une fonction). L’utilisation de default=[] n’est pas correct car cela crée une valeur par défaut mutante qui est partagée entre les instances de ArrayField.

base_field

Ce paramètre est obligatoire.

Définit le type de données sous-jacent et le comportement du tableau. Il devrait s’agir d’une instance d’une sous-classe de Field. Par exemple, cela pourrait être un IntegerField ou un CharField. La plupart des types de champs sont autorisés, à l’exception de ceux qui gèrent des données relationnelles (ForeignKey, OneToOneField et ManyToManyField).

Il est possible d’imbriquer des champs tabulaires, c’est-à-dire que base_field peut contenir une instance de ArrayField. Par exemple :

from django.db import models
from django.contrib.postgres.fields import ArrayField

class ChessBoard(models.Model):
    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )

La transformation de valeurs entre la base de données et le modèle, la validation de données et la configuration ainsi que la sérialisation sont tous délégués au champ de base sous-jacent.

size

Ce paramètre est facultatif.

Si présent, le tableau aura la taille maximale indiquée. Cette valeur est transmise à la base de données, même si PostgreSQL n’applique actuellement pas cette restriction.

Note

Lors de l’imbrication de champs ArrayField, que le paramètre size soit défini ou non, PostgreSQL exige que les tableaux soient rectangulaires :

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 des formes irrégulières sont nécessaires, le sous-champ doit pouvoir être nul et les valeurs doivent être complétées avec None.

Interrogation de ArrayField

Il existe plusieurs recherches et transformations spécifiques au champ ArrayField. Nous allons utiliser le modèle d’exemple suivant :

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

contains

La recherche contains est surchargée pour ArrayField. Les objets renvoyés sont ceux pour qui les valeurs transmises forment un sous-ensemble des données. Elle utilise l’opérateur SQL @>. Par exemple :

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

contained_by

Il s’agit de l’inverse de la recherche contains, les objets renvoyés sont ceux pour qui les données forment un sous-ensemble des valeurs transmises. Elle utilise l’opérateur SQL <@. Par exemple :

>>> 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: Second post>]

>>> Post.objects.filter(tags__contained_by=['thoughts', 'django', 'tutorial'])
[<Post: First post>, <Post: Second post>, <Post: Third post>]

overlap

Renvoie les objets dont les données contiennent au moins une valeur en commun avec celles transmises. Utilise l’opérateur SQL &&. Par exemple :

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

len

Renvoie la longueur du tableau. Les recherches disponibles à la suite de celle-ci sont celles qui sont disponibles pour les champs IntegerField. Par exemple :

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

Transformations d’indice

Cette classe de transformations permet d’indicer les tableaux dans les requêtes. Tout nombre entier non négatif est utilisable. Aucune erreur n’est produite si l’indice dépasse la taille size du tableau. Les recherches disponibles à la suite de la transformation sont celles du champ base_field. Par exemple :

>>> 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 utilise l’indiçage à partir de 1 pour les champs de type tableau lors de l’écriture de SQL brut. Cependant, ces indices ainsi que ceux utilisés dans les segmentations emploient des indices depuis 0 afin de rester cohérent avec Python.

Transformations de segmentation

Cette classe de transformation permet de segmenter un tableau. Toute paire de nombres entiers non négatifs peut être utilisée, séparée par un seul soulignement. Les recherches disponibles après la transformation ne changent pas. Par exemple :

>>> 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: Second post>]

>>> Post.objects.filter(tags__0_2__contains=['thoughts'])
[<Post: First post>, <Post: Second post>]

Note

PostgreSQL utilise l’indiçage à partir de 1 pour les champs de type tableau lors de l’écriture de SQL brut. Cependant, ces segmentations ainsi que celles utilisées dans les indices emploient des indices depuis 0 afin de rester cohérent avec Python.

Indices et segmentations avec les tableaux multidimensionnels

Le comportement de PostgreSQL est plutôt ésotérique lorsqu’il s’agit d’indicer ou de segmenter des tableaux à plusieurs dimensions. L’emploi des indices pour atteindre une donnée imbriquée fonctionne dans tous les cas, mais la plupart des autres segmentations se comportent bizarrement au niveau de la base de données et ne peuvent pas être prises en charge de manière cohérente et logique par Django.

Indexation des champs ArrayField

Actuellement, l’utilisation de db_index crée un index btree. Cela n’est pas particulièrement utile pour l’interrogation. Un index plus utile est le type GIN, qu’il est possible de créer en utilisant une opération RunSQL.

HStoreField

class HStoreField(**options)

Un champ pour stocker des correspondances de chaîne à chaîne. Le type de données Python utilisé est un dict.

Pour utiliser ce champ, il faut :

  1. Ajouter 'django.contrib.postgres' à votre réglage INSTALLED_APPS.

  2. Configurer l’extension hstore dans PostgreSQL avant la première opération CreateModel ou AddField en ajoutant une migration comprenant l’opération HStoreExtension. Par exemple :

    from django.contrib.postgres.operations import HStoreExtension
    
    class Migration(migrations.Migration):
        ...
    
        operations = [
            HStoreExtension(),
            ...
        ]
    

    La création de l’extension exige un utilisateur de base de données possédant les droits de superutilisateur. Si l’utilisateur de base de données Django ne possède pas ces privilèges, il est alors nécessaire de créer l’extension en dehors des migrations Django avec un utilisateur qui possède les droits requis. Dans ce cas, connectez-vous à la base de données et lancez la requête CREATE EXTENSION IF NOT EXISTS hstore;.

Vous verrez une erreur du genre can't adapt type 'dict' si vous omettez la première étape, ou type "hstore" does not exist si vous omettez la seconde.

Note

Parfois, il est utile de pouvoir limiter les clés admises comme valides pour un champ donné. Cela peut se faire à l’aide de KeysValidator.

Interrogation de HStoreField

En plus de la possibilité d’interroger en fonction de la clé, il existe quelques recherches tout exprès pour les champs HStoreField.

Nous allons utiliser le modèle d’exemple suivant :

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

Recherches par clé

Pour rechercher en fonction d’une clé donnée, il suffit d’utiliser cette clé comme nom de requête :

>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
>>> Dog.objects.create(name='Meg', data={'breed': 'collie'})

>>> Dog.objects.filter(data__breed='collie')
[<Dog: Meg>]

Il est possible d’enchaîner d’autres recherches après une recherche par clé :

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

Si la clé avec laquelle vous souhaitez rechercher entre en conflit avec le nom d’une recherche existante, vous devez utiliser la recherche hstorefield.contains à la place.

Avertissement

Comme n’importe quelle chaîne peut être une clé d’une valeur hstore, toute recherche autre que celles mentionnées ci-dessous sera interprétée comme une recherche par clé. Aucune erreur n’est produite. Faites spécialement attention aux erreurs de frappe et vérifiez systématiquement que vos requêtes fonctionnent correctement.

contains

La recherche contains est surchargée pour HStoreField. Les objets renvoyés sont ceux pour qui le dictionnaire de paires clé-valeur est totalement contenu dans le champ. Elle utilise l’opérateur SQL @>. Par exemple :

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

contained_by

Il s’agit de l’inverse de la recherche contains, les objets renvoyés sont ceux pour qui les paires de clé-valeur forment un sous-ensemble des valeurs transmises. Elle utilise l’opérateur SQL <@. Par exemple :

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

has_key

Renvoie les objets dont les données contiennent la clé indiquée. Utilise l’opérateur SQL ?. Par exemple :

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

has_keys

Renvoie les objets dont les données contiennent toutes les clés indiquées. Utilise l’opérateur SQL ?&. Par exemple :

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

keys

Renvoie l’ensemble des clés des objets concernés. Notez que l’ordre n’est pas garanti, ce qui fait que cette transformation est surtout utile quand elle est combinée à des recherches sur ArrayField. Utilise la fonction SQL akeys(). Par exemple :

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

values

Renvoie l’ensemble des valeurs des objets concernés. Notez que l’ordre n’est pas garanti, ce qui fait que cette transformation est surtout utile quand elle est combinée à des recherches sur ArrayField. Utilise la fonction SQL avalues(). Par exemple :

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

Champs d’intervalle

Il existe cinq types de champs d’intervalle, correspondant au types d’intervalles natifs de PostgreSQL. Ces champs sont utilisés pour stocker un intervalle de valeurs ; par exemple, l’heure de début et de fin d’un événement ou l’intervalle des âges appropriés à une activité.

Tous les champs d’intervalle produisent des objets Range psycopg2 en Python, mais acceptent aussi des tuples en entrée si aucune information de limite n’est nécessaire. Par défaut, la limite inférieure est incluse et la limite supérieure exclue.

IntegerRangeField

class IntegerRangeField(**options)

Stocke un intervalle de nombres entiers. Basé sur un IntegerField. Représenté par un int4range dans la base de données et un NumericRange en Python.

BigIntegerRangeField

class BigIntegerRangeField(**options)

Stocke un intervalle de nombres entiers étendus. Basé sur un BigIntegerField. Représenté par un int8range dans la base de données et un NumericRange en Python.

FloatRangeField

class FloatRangeField(**options)

Stocke un intervalle de valeurs en virgule flottante. Basé sur un FloatField. Représenté par un numrange dans la base de données et un NumericRange en Python.

DateTimeRangeField

class DateTimeRangeField(**options)

Stocke un intervalle d’horodatages. Basé sur un DateTimeField. Représenté par un tztsrange dans la base de données et un DateTimeTZRange en Python.

DateRangeField

class DateRangeField(**options)

Stocke un intervalle de dates. Basé sur un DateField. Représenté par un daterange dans la base de données et un DateRange en Python.

Interrogation de champs d’intervalle

Il existe un certain nombre de recherches et de transformations spécifiques aux champs d’intervalle. Ils sont disponibles pour tous les champs ci-dessus, mais nous allons utiliser le modèle d’exemple suivant :

from django.contrib.postgres.fields import IntegerRangeField
from django.db import models

class Event(models.Model):
    name = models.CharField(max_length=200)
    ages = IntegerRangeField()

    def __str__(self):  # __unicode__ on Python 2
        return self.name

Nous allons aussi utiliser les objets d’exemple suivants :

>>> Event.objects.create(name='Soft play', ages=(0, 10))
>>> Event.objects.create(name='Pub trip', ages=(21, None))

et NumericRange:

>>> from psycopg2.extras import NumericRange

Fonctions de contenance

Comme pour les autres champs PostgreSQL, trois opérateurs de contenance standard sont disponibles : contains, contained_by et overlap, utilisant respectivement les opérateurs SQL @>, <@ et &&.

contains
>>> Event.objects.filter(ages__contains=NumericRange(4, 5))
[<Event: Soft play>]
contained_by
>>> Event.objects.filter(ages__contained_by=NumericRange(0, 15))
[<Event: Soft play>]
overlap
>>> Event.objects.filter(ages__overlap=NumericRange(8, 12))
[<Event: Soft play>]

Fonctions de comparaison

Les champs d’intervalle prennent en charge les recherches standard : lt, gt, lte et gte. Elles ne sont pas particulièrement utiles, la comparaison s’opère d’abord sur la limite inférieure et si nécessaire, sur la limite supérieure ensuite. C’est également la stratégie adoptée pour trier selon un champ d’intervalle. Il est recommandé d’utiliser plutôt les opérateurs spécifiques de comparaison d’intervalle.

fully_lt

Les intervalles renvoyés sont strictement inférieurs à l’intervalle transmis. En d’autres termes, tous les points d’un intervalle renvoyé sont plus petits que tous ceux de l’intervalle transmis.

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

Les intervalles renvoyés sont strictement supérieurs à l’intervalle transmis. En d’autres termes, tous les points d’un intervalle renvoyé sont plus grands que tous ceux de l’intervalle transmis.

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

Les intervalles renvoyés ne contiennent aucun élément plus petit que ceux de l’intervalle transmis, c’est-à-dire que la limite inférieure d’un intervalle renvoyé est au moins égale à la limite inférieure de l’intervalle transmis.

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

Les intervalles renvoyés ne contiennent aucun élément plus grand que ceux de l’intervalle transmis, c’est-à-dire que la limite supérieure d’un intervalle renvoyé n’est pas plus grande que la limite supérieure de l’intervalle transmis.

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

Les intervalles renvoyés partagent une limite avec l’intervalle transmis.

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

Interrogation à l’aide des limites

Trois transformations sont disponibles pour être utilisées dans les requêtes. Vous pouvez extraire la limite inférieure ou supérieure, ou interroger sur la base de l’intervalle vide.

startswith

Les objets renvoyés possèdent la limite inférieure indiquée. Peut être suivie par des recherches valides pour le champ de base.

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

Les objets renvoyés possèdent la limite supérieure indiquée. Peut être suivie par des recherches valides pour le champ de base.

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

Les objets renvoyés sont des intervalles vides. Peut être suivie par des recherches valides pour un champ BooleanField.

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

Définition de ses propres types d’intervalle

PostgreSQL permet de définir ses propres types d’intervalle. Les implémentations de champs de modèle et de formulaire de Django utilisent les classes de base ci-dessous, et psycopg2 propose une fonction register_range() pour permettre l’utilisation de types d’intervalle personnalisés.

class RangeField(**options)

Classe de base pour les champs de modèle de type intervalle.

base_field

Le champ de modèle à utiliser.

range_type

Le type d’intervalle psycopg2 à utiliser.

form_field

La classe de champ de formulaire à utiliser. Doit être une sous-classe de django.contrib.postgres.forms.BaseRangeField.

class django.contrib.postgres.forms.BaseRangeField

Classe de base pour les champs de formulaire de type intervalle.

base_field

Le champ de formulaire à utiliser.

range_type

Le type d’intervalle psycopg2 à utiliser.

Back to Top