Fonctions de base de données

Les classes documentées ci-dessous permettent d’exploiter dans Django les fonctions fournies par la base de données sous-jacente telles que les annotations, les agrégations ou les filtres. Les fonctions sont également des expressions, elles peuvent donc être utilisées et combinées avec d’autres expressions comme les fonctions d’agrégation.

Nous allons utiliser le modèle suivant dans les exemples de chaque fonction :

class Author(models.Model):
    name = models.CharField(max_length=50)
    age = models.PositiveIntegerField(null=True, blank=True)
    alias = models.CharField(max_length=50, null=True, blank=True)
    goes_by = models.CharField(max_length=50, null=True, blank=True)

Nous ne recommandons généralement pas de définir null=True pour les champs CharField car cela permet au champ de posséder deux valeurs « vides » différentes, mais nous l’utilisons ici pour l’exemple Coalesce ci-dessous.

Fonctions de comparaison et de conversion

Cast

class Cast(expression, output_field)[source]

Force le type de résultat de expression à celui de output_field.

Exemple d’utilisation :

>>> from django.db.models import FloatField
>>> from django.db.models.functions import Cast
>>> Value.objects.create(integer=4)
>>> value = Value.objects.annotate(as_float=Cast('integer', FloatField())).get()
>>> print(value.as_float)
4.0

Coalesce

class Coalesce(*expressions, **extra)[source]

Accepte une liste d’au moins deux noms de champ ou expressions et renvoie la première valeur non nulle (notez qu’une chaîne vide n’est pas considérée comme une valeur nulle). Chaque paramètre doit être d’un type similaire ; si vous mélangez des textes et des nombres, la base de données produira une erreur.

Exemples d’utilisation :

>>> # Get a screen name from least to most public
>>> from django.db.models import Sum, Value as V
>>> from django.db.models.functions import Coalesce
>>> Author.objects.create(name='Margaret Smith', goes_by='Maggie')
>>> author = Author.objects.annotate(
...    screen_name=Coalesce('alias', 'goes_by', 'name')).get()
>>> print(author.screen_name)
Maggie

>>> # Prevent an aggregate Sum() from returning None
>>> aggregated = Author.objects.aggregate(
...    combined_age=Coalesce(Sum('age'), V(0)),
...    combined_age_default=Sum('age'))
>>> print(aggregated['combined_age'])
0
>>> print(aggregated['combined_age_default'])
None

Avertissement

Avec MySQL, il est possible qu’une valeur Python transmise à Coalesce soit convertie en un type incorrect, sauf dans le cas où le bon type de base de données est explicitement forcé :

>>> from django.db.models import DateTimeField
>>> from django.db.models.functions import Cast, Coalesce
>>> from django.utils import timezone
>>> now = timezone.now()
>>> Coalesce('updated', Cast(now, DateTimeField()))

Greatest

class Greatest(*expressions, **extra)[source]

Accepte une liste d’au moins deux noms de champ ou expressions et renvoie la plus grande valeur. Chaque paramètre doit être d’un type similaire ; si vous mélangez des textes et des nombres, la base de données produira une erreur.

Exemple d’utilisation :

class Blog(models.Model):
    body = models.TextField()
    modified = models.DateTimeField(auto_now=True)

class Comment(models.Model):
    body = models.TextField()
    modified = models.DateTimeField(auto_now=True)
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)

>>> from django.db.models.functions import Greatest
>>> blog = Blog.objects.create(body='Greatest is the best.')
>>> comment = Comment.objects.create(body='No, Least is better.', blog=blog)
>>> comments = Comment.objects.annotate(last_updated=Greatest('modified', 'blog__modified'))
>>> annotated_comment = comments.get()

annotated_comment.last_updated sera la valeur la plus récente entre blog.modified et comment.modified.

Avertissement

Le comportement de Greatest varie entre les bases de données lorsqu’une ou plusieurs des expressions ont la valeur null:

  • PostgreSQL : Greatest renvoie l’expression non nulle la plus grande, ou null si toutes les expressions valent null.
  • SQLite, Oracle et MySQL : si l’une des expressions vaut null, Greatest renvoie null.

Le comportement de PostgreSQL peut être émulé à l’aide de Coalesce si vous avez une valeur minimum adéquate à fournir comme valeur par défaut.

Least

class Least(*expressions, **extra)[source]

Accepte une liste d’au moins deux noms de champ ou expressions et renvoie la plus petite valeur. Chaque paramètre doit être d’un type similaire ; si vous mélangez des textes et des nombres, la base de données produira une erreur.

Avertissement

Le comportement de Least varie entre les bases de données lorsqu’une ou plusieurs des expressions ont la valeur null:

  • PostgreSQL : Least renvoie l’expression non nulle la plus petite, ou null si toutes les expressions valent null.
  • SQLite, Oracle et MySQL : si l’une des expressions vaut null, Least renvoie null.

Le comportement de PostgreSQL peut être émulé à l’aide de Coalesce si vous avez une valeur maximum adéquate à fournir comme valeur par défaut.

Fonctions de date

Nous allons utiliser le modèle suivant dans les exemples de chaque fonction :

class Experiment(models.Model):
    start_datetime = models.DateTimeField()
    start_date = models.DateField(null=True, blank=True)
    start_time = models.TimeField(null=True, blank=True)
    end_datetime = models.DateTimeField(null=True, blank=True)
    end_date = models.DateField(null=True, blank=True)
    end_time = models.TimeField(null=True, blank=True)

Extract

class Extract(expression, lookup_name=None, tzinfo=None, **extra)[source]

Extrait un composant de date sous forme de nombre.

Accepte une expression représentant un champ DateField, DateTimeField, TimeField ou DurationField ainsi qu’un nom d’expression lookup_name, et renvoie la partie de date référencée par lookup_name sous forme de champ IntegerField. Django utilise habituellement la fonction d’extraction des bases de données, il est donc possible d’utiliser n’importe quel nom lookup_name pris en charge par la base de données en cours. Il est possible de passer aussi une sous-classe de tzinfo, habituellement fournie par pytz, pour extraire une valeur dans un fuseau horaire spécifique.

Changed in Django 2.0:

La prise en charge de DurationField a été ajoutée.

Étant donnée la date/heure 2015-06-15 23:30:01.000321+00:00, les valeurs lookup_name possibles renvoient :

  • « year » (année) : 2015
  • « quarter »: 2
  • « month » (mois) : 6
  • « day » (jour) : 15
  • « week »: 25
  • « week_day » (jour de semaine) : 2
  • « hour » (heure) : 23
  • « minute »: 30
  • « second »: 1

Si un fuseau horaire différent comme Australia/Melbourne est actif dans Django, la date/heure est convertie dans le fuseau avant que la valeur soit extraite. Le décalage horaire de Melbourne dans la date d’exemple ci-dessus est +10:00. Les valeurs renvoyées lorsque ce fuseau est actif seront les mêmes que ci-dessus, à l’exception de :

  • « day »: 16
  • « week_day »: 3
  • « hour »: 9

Valeurs week_day

L’extraction week_day est calculée différemment de la plupart des bases de données et des fonctions Python standards. Cette fonction renvoie 1 pour dimanche, 2 pour lundi, jusqu’à 7 pour samedi.

Le calcul équivalent en Python est :

>>> from datetime import datetime
>>> dt = datetime(2015, 6, 15)
>>> (dt.isoweekday() % 7) + 1
2

Valeurs week

The week lookup_type is calculated based on ISO-8601, i.e., a week starts on a Monday. The first week of a year is the one that contains the year’s first Thursday, i.e. the first week has the majority (four or more) of its days in the year. The value returned is in the range 1 to 52 or 53.

Chaque valeur lookup_name ci-dessus possède une sous-classe de Extract correspondante (énumérées ci-dessous) qui devrait être utilisée au lieu de l’équivalent plus bavard, c’est-à-dire ExtractYear(...) au lieu de Extract(..., lookup_name='year').

Exemple d’utilisation :

>>> from datetime import datetime
>>> from django.db.models.functions import Extract
>>> start = datetime(2015, 6, 15)
>>> end = datetime(2015, 7, 2)
>>> Experiment.objects.create(
...    start_datetime=start, start_date=start.date(),
...    end_datetime=end, end_date=end.date())
>>> # Add the experiment start year as a field in the QuerySet.
>>> experiment = Experiment.objects.annotate(
...    start_year=Extract('start_datetime', 'year')).get()
>>> experiment.start_year
2015
>>> # How many experiments completed in the same year in which they started?
>>> Experiment.objects.filter(
...    start_datetime__year=Extract('end_datetime', 'year')).count()
1

Extractions DateField

class ExtractYear(expression, tzinfo=None, **extra)[source]
lookup_name = 'year'
class ExtractMonth(expression, tzinfo=None, **extra)[source]
lookup_name = 'month'
class ExtractDay(expression, tzinfo=None, **extra)[source]
lookup_name = 'day'
class ExtractWeekDay(expression, tzinfo=None, **extra)[source]
lookup_name = 'week_day'
class ExtractWeek(expression, tzinfo=None, **extra)[source]
New in Django 1.11.
lookup_name = 'week'
class ExtractQuarter(expression, tzinfo=None, **extra)[source]
New in Django 2.0.
lookup_name = 'quarter'

Il s’agit d’équivalents logiques à Extract('champ_date', lookup_name). Chaque classe est aussi une classe Transform inscrite pour les champs DateField et DateTimeField comme __(lookup_name), par ex. __year.

Comme les champs DateField n’ont pas de composant d’heure, seules les sous-classes de Extract qui s’appliquent aux parties de date peuvent être utilisées avec DateField:

>>> from datetime import datetime
>>> from django.utils import timezone
>>> from django.db.models.functions import (
...     ExtractDay, ExtractMonth, ExtractQuarter, ExtractWeek,
...     ExtractWeekDay, ExtractYear,
... )
>>> start_2015 = datetime(2015, 6, 15, 23, 30, 1, tzinfo=timezone.utc)
>>> end_2015 = datetime(2015, 6, 16, 13, 11, 27, tzinfo=timezone.utc)
>>> Experiment.objects.create(
...    start_datetime=start_2015, start_date=start_2015.date(),
...    end_datetime=end_2015, end_date=end_2015.date())
>>> Experiment.objects.annotate(
...     year=ExtractYear('start_date'),
...     quarter=ExtractQuarter('start_date'),
...     month=ExtractMonth('start_date'),
...     week=ExtractWeek('start_date'),
...     day=ExtractDay('start_date'),
...     weekday=ExtractWeekDay('start_date'),
... ).values('year', 'quarter', 'month', 'week', 'day', 'weekday').get(
...     end_date__year=ExtractYear('start_date'),
... )
{'year': 2015, 'quarter': 2, 'month': 6, 'week': 25, 'day': 15, 'weekday': 2}

Extractions DateTimeField

En plus de ce qui suit, toutes les extractions pour DateField énumérées ci-dessus peuvent aussi être utilisées pour DateTimeField.

class ExtractHour(expression, tzinfo=None, **extra)[source]
lookup_name = 'hour'
class ExtractMinute(expression, tzinfo=None, **extra)[source]
lookup_name = 'minute'
class ExtractSecond(expression, tzinfo=None, **extra)[source]
lookup_name = 'second'

Il s’agit d’équivalents logiques à Extract('champ_datetime', lookup_name). Chaque classe est aussi une classe Transform inscrite pour le champ DateTimeField comme __(lookup_name), par ex. __minute.

Exemples DateTimeField:

>>> from datetime import datetime
>>> from django.utils import timezone
>>> from django.db.models.functions import (
...     ExtractDay, ExtractHour, ExtractMinute, ExtractMonth,
...     ExtractQuarter, ExtractSecond, ExtractWeek, ExtractWeekDay,
...     ExtractYear,
... )
>>> start_2015 = datetime(2015, 6, 15, 23, 30, 1, tzinfo=timezone.utc)
>>> end_2015 = datetime(2015, 6, 16, 13, 11, 27, tzinfo=timezone.utc)
>>> Experiment.objects.create(
...    start_datetime=start_2015, start_date=start_2015.date(),
...    end_datetime=end_2015, end_date=end_2015.date())
>>> Experiment.objects.annotate(
...     year=ExtractYear('start_datetime'),
...     quarter=ExtractQuarter('start_datetime'),
...     month=ExtractMonth('start_datetime'),
...     week=ExtractWeek('start_datetime'),
...     day=ExtractDay('start_datetime'),
...     weekday=ExtractWeekDay('start_datetime'),
...     hour=ExtractHour('start_datetime'),
...     minute=ExtractMinute('start_datetime'),
...     second=ExtractSecond('start_datetime'),
... ).values(
...     'year', 'month', 'week', 'day', 'weekday', 'hour', 'minute', 'second',
... ).get(end_datetime__year=ExtractYear('start_datetime'))
{'year': 2015, 'quarter': 2, 'month': 6, 'week': 25, 'day': 15, 'weekday': 2,
 'hour': 23, 'minute': 30, 'second': 1}

Lorsque USE_TZ vaut True, les dates/heures sont stockées en UTC dans la base de données. Si un autre fuseau horaire est actif dans Django, la date/heure est convertie dans ce fuseau avant que la valeur soit extraite. L’exemple ci-dessous convertit dans le fuseau horaire de Melbourne (UTC +10:00), ce qui modifie les valeurs du jour, du jour de la semaine et de l’heure qui sont renvoyées :

>>> import pytz
>>> melb = pytz.timezone('Australia/Melbourne')  # UTC+10:00
>>> with timezone.override(melb):
...    Experiment.objects.annotate(
...        day=ExtractDay('start_datetime'),
...        weekday=ExtractWeekDay('start_datetime'),
...        hour=ExtractHour('start_datetime'),
...    ).values('day', 'weekday', 'hour').get(
...        end_datetime__year=ExtractYear('start_datetime'),
...    )
{'day': 16, 'weekday': 3, 'hour': 9}

Le passage explicite du fuseau horaire à la fonction Extract fonctionne de la même manière et prend la priorité sur le fuseau horaire actif :

>>> import pytz
>>> melb = pytz.timezone('Australia/Melbourne')
>>> Experiment.objects.annotate(
...     day=ExtractDay('start_datetime', tzinfo=melb),
...     weekday=ExtractWeekDay('start_datetime', tzinfo=melb),
...     hour=ExtractHour('start_datetime', tzinfo=melb),
... ).values('day', 'weekday', 'hour').get(
...     end_datetime__year=ExtractYear('start_datetime'),
... )
{'day': 16, 'weekday': 3, 'hour': 9}

Now

class Now[source]

Renvoie la date et l’heure courante du serveur de base de données au moment où la requête est exécutée, typiquement en utilisant le code SQL CURRENT_TIMESTAMP.

Exemple d’utilisation :

>>> from django.db.models.functions import Now
>>> Article.objects.filter(published__lte=Now())
<QuerySet [<Article: How to Django>]>

Considérations sur PostgreSQL

Avec PostgreSQL, l’instruction CURRENT_TIMESTAMP renvoie l’heure du début de la transaction actuelle. Par conséquent, pour garantir la compatibilité entre bases de données, Now() utilise STATEMENT_TIMESTAMP à la place. Si vous avez besoin de l’horodatage de la transaction, utilisez django.contrib.postgres.functions.TransactionNow.

Trunc

class Trunc(expression, kind, output_field=None, tzinfo=None, **extra)[source]

Tronque une date jusqu’à un composant significatif.

Lorsque vous ne voulez que savoir si quelque chose s’est produit durant une année, une heure ou un jour particulier, mais pas à quelle seconde, Trunc (et ses sous-classes) peut être utile pour filtrer ou agréger vos données. Par exemple, vous pouvez utiliser Trunc pour calculer le nombre de ventes par jour.

Trunc accepte une seule expression, représentant un champ DateField, TimeField ou DateTimeField, une variable kind représentant une partie de date ou d’heure et un champ output_field qui vaut DateTimeField(), TimeField()` ou DateField(). Elle renvoie une date, une heure ou une date/heure en fonction de output_field, avec les champs jusqu’à kind définis à leur valeur minimale. Si output_field est omis, la valeur par défaut sera le type output_field de expression. Il est possible de passer aussi une sous-classe de tzinfo, habituellement fournie par pytz, pour tronquer une valeur dans un fuseau horaire spécifique.

Étant donnée la date/heure 2015-06-15 14:30:50.000321+00:00, les valeurs kind possibles renvoient :

  • « year »: 2015-01-01 00:00:00+00:00
  • « quarter »: 2015-04-01 00:00:00+00:00
  • « month »: 2015-06-01 00:00:00+00:00
  • « day »: 2015-06-15 00:00:00+00:00
  • « hour »: 2015-06-15 14:00:00+00:00
  • « minute »: 2015-06-15 14:30:00+00:00
  • « second »: 2015-06-15 14:30:50+00:00

Si un fuseau horaire différent comme Australia/Melbourne est actif dans Django, la date/heure est convertie dans le nouveau fuseau avant que la valeur soit tronquée. Le décalage horaire de Melbourne dans la date d’exemple ci-dessus est +10:00. Les valeurs renvoyées lorsque ce fuseau est actif seront :

  • « year »: 2015-01-01 00:00:00+11:00
  • « quarter »: 2015-04-01 00:00:00+10:00
  • « month »: 2015-06-01 00:00:00+10:00
  • « day »: 2015-06-16 00:00:00+10:00
  • « hour »: 2015-06-16 00:00:00+10:00
  • « minute »: 2015-06-16 00:30:00+10:00
  • « second »: 2015-06-16 00:30:50+10:00

L’année présente un décalage de +11:00 parce que le résultat traverse un passage à l’heure d’été.

Chaque valeur kind ci-dessus possède une sous-classe de Trunc correspondante (énumérées ci-dessous) qui devrait être utilisée au lieu de l’équivalent plus bavard, c’est-à-dire TruncYear(...) au lieu de Trunc(..., kind='year').

Les sous-classes sont toutes définies comme des transformations, mais elles ne sont inscrites pour aucun champ, car les noms de requête qui s’imposent sont déjà réservés par les sous-classes de Extract.

Exemple d’utilisation :

>>> from datetime import datetime
>>> from django.db.models import Count, DateTimeField
>>> from django.db.models.functions import Trunc
>>> Experiment.objects.create(start_datetime=datetime(2015, 6, 15, 14, 30, 50, 321))
>>> Experiment.objects.create(start_datetime=datetime(2015, 6, 15, 14, 40, 2, 123))
>>> Experiment.objects.create(start_datetime=datetime(2015, 12, 25, 10, 5, 27, 999))
>>> experiments_per_day = Experiment.objects.annotate(
...    start_day=Trunc('start_datetime', 'day', output_field=DateTimeField())
... ).values('start_day').annotate(experiments=Count('id'))
>>> for exp in experiments_per_day:
...     print(exp['start_day'], exp['experiments'])
...
2015-06-15 00:00:00 2
2015-12-25 00:00:00 1
>>> experiments = Experiment.objects.annotate(
...    start_day=Trunc('start_datetime', 'day', output_field=DateTimeField())
... ).filter(start_day=datetime(2015, 6, 15))
>>> for exp in experiments:
...     print(exp.start_datetime)
...
2015-06-15 14:30:50.000321
2015-06-15 14:40:02.000123

Troncature de DateField

class TruncYear(expression, output_field=None, tzinfo=None, **extra)[source]
kind = 'year'
class TruncMonth(expression, output_field=None, tzinfo=None, **extra)[source]
kind = 'month'
class TruncQuarter(expression, output_field=None, tzinfo=None, **extra)[source]
New in Django 2.0.
kind = 'quarter'

Ce sont des équivalences logiques à Trunc('champ_date', kind). Elles tronquent toutes les parties de la date jusqu’à kind, ce qui permet de grouper ou de filtrer des dates avec une plus faible précision. expression peut avoir une valeur output_field de DateField ou de DateTimeField.

Comme les champs DateField n’ont pas de composant d’heure, seules les sous-classes de Trunc qui s’appliquent aux parties de date peuvent être utilisées avec DateField:

>>> from datetime import datetime
>>> from django.db.models import Count
>>> from django.db.models.functions import TruncMonth, TruncYear
>>> from django.utils import timezone
>>> start1 = datetime(2014, 6, 15, 14, 30, 50, 321, tzinfo=timezone.utc)
>>> start2 = datetime(2015, 6, 15, 14, 40, 2, 123, tzinfo=timezone.utc)
>>> start3 = datetime(2015, 12, 31, 17, 5, 27, 999, tzinfo=timezone.utc)
>>> Experiment.objects.create(start_datetime=start1, start_date=start1.date())
>>> Experiment.objects.create(start_datetime=start2, start_date=start2.date())
>>> Experiment.objects.create(start_datetime=start3, start_date=start3.date())
>>> experiments_per_year = Experiment.objects.annotate(
...    year=TruncYear('start_date')).values('year').annotate(
...    experiments=Count('id'))
>>> for exp in experiments_per_year:
...     print(exp['year'], exp['experiments'])
...
2014-01-01 1
2015-01-01 2

>>> import pytz
>>> melb = pytz.timezone('Australia/Melbourne')
>>> experiments_per_month = Experiment.objects.annotate(
...    month=TruncMonth('start_datetime', tzinfo=melb)).values('month').annotate(
...    experiments=Count('id'))
>>> for exp in experiments_per_month:
...     print(exp['month'], exp['experiments'])
...
2015-06-01 00:00:00+10:00 1
2016-01-01 00:00:00+11:00 1
2014-06-01 00:00:00+10:00 1

Troncature de DateTimeField

class TruncDate(expression, **extra)[source]
lookup_name = 'date'
output_field = DateField()

TruncDate force le type de expression à une date plutôt que d’utiliser l’instruction SQL de troncature. Elle est aussi inscrite comme transformation pour DateTimeField sous la forme __date.

class TruncTime(expression, **extra)[source]
New in Django 1.11:
lookup_name = 'time'
output_field = TimeField()

TruncTime force le type de expression à une heure plutôt que d’utiliser l’instruction SQL de troncature. Elle est aussi inscrite comme transformation pour DateTimeField sous la forme __time.

class TruncDay(expression, output_field=None, tzinfo=None, **extra)[source]
kind = 'day'
class TruncHour(expression, output_field=None, tzinfo=None, **extra)[source]
kind = 'hour'
class TruncMinute(expression, output_field=None, tzinfo=None, **extra)[source]
kind = 'minute'
class TruncSecond(expression, output_field=None, tzinfo=None, **extra)[source]
kind = 'second'

Ce sont des équivalences logiques à Trunc('champ_dateheure', kind). Elles tronquent toutes les parties de la date jusqu’à kind, ce qui permet de grouper ou de filtrer des dates/heures avec une plus faible précision. expression doit avoir une valeur output_field de DateTimeField.

Exemple d’utilisation :

>>> from datetime import date, datetime
>>> from django.db.models import Count
>>> from django.db.models.functions import (
...     TruncDate, TruncDay, TruncHour, TruncMinute, TruncSecond,
... )
>>> from django.utils import timezone
>>> import pytz
>>> start1 = datetime(2014, 6, 15, 14, 30, 50, 321, tzinfo=timezone.utc)
>>> Experiment.objects.create(start_datetime=start1, start_date=start1.date())
>>> melb = pytz.timezone('Australia/Melbourne')
>>> Experiment.objects.annotate(
...     date=TruncDate('start_datetime'),
...     day=TruncDay('start_datetime', tzinfo=melb),
...     hour=TruncHour('start_datetime', tzinfo=melb),
...     minute=TruncMinute('start_datetime'),
...     second=TruncSecond('start_datetime'),
... ).values('date', 'day', 'hour', 'minute', 'second').get()
{'date': datetime.date(2014, 6, 15),
 'day': datetime.datetime(2014, 6, 16, 0, 0, tzinfo=<DstTzInfo 'Australia/Melbourne' AEST+10:00:00 STD>),
 'hour': datetime.datetime(2014, 6, 16, 0, 0, tzinfo=<DstTzInfo 'Australia/Melbourne' AEST+10:00:00 STD>),
 'minute': 'minute': datetime.datetime(2014, 6, 15, 14, 30, tzinfo=<UTC>),
 'second': datetime.datetime(2014, 6, 15, 14, 30, 50, tzinfo=<UTC>)
}

Troncature de TimeField

New in Django 1.11.
class TruncHour(expression, output_field=None, tzinfo=None, **extra)[source]
kind = 'hour'
class TruncMinute(expression, output_field=None, tzinfo=None, **extra)[source]
kind = 'minute'
class TruncSecond(expression, output_field=None, tzinfo=None, **extra)[source]
kind = 'second'

Ce sont des équivalences logiques à Trunc('champ_heure', kind). Elles tronquent toutes les parties de l’heure jusqu’à kind, ce qui permet de grouper ou de filtrer des heures avec une plus faible précision. expression peut avoir une valeur output_field de TimeField ou de DateTimeField.

Comme les champs TimeField n’ont pas de composant de date, seules les sous-classes de Trunc qui s’appliquent aux parties d’heure peuvent être utilisées avec TimeField:

>>> from datetime import datetime
>>> from django.db.models import Count, TimeField
>>> from django.db.models.functions import TruncHour
>>> from django.utils import timezone
>>> start1 = datetime(2014, 6, 15, 14, 30, 50, 321, tzinfo=timezone.utc)
>>> start2 = datetime(2014, 6, 15, 14, 40, 2, 123, tzinfo=timezone.utc)
>>> start3 = datetime(2015, 12, 31, 17, 5, 27, 999, tzinfo=timezone.utc)
>>> Experiment.objects.create(start_datetime=start1, start_time=start1.time())
>>> Experiment.objects.create(start_datetime=start2, start_time=start2.time())
>>> Experiment.objects.create(start_datetime=start3, start_time=start3.time())
>>> experiments_per_hour = Experiment.objects.annotate(
...    hour=TruncHour('start_datetime', output_field=TimeField()),
... ).values('hour').annotate(experiments=Count('id'))
>>> for exp in experiments_per_hour:
...     print(exp['hour'], exp['experiments'])
...
14:00:00 2
17:00:00 1

>>> import pytz
>>> melb = pytz.timezone('Australia/Melbourne')
>>> experiments_per_hour = Experiment.objects.annotate(
...    hour=TruncHour('start_datetime', tzinfo=melb),
... ).values('hour').annotate(experiments=Count('id'))
>>> for exp in experiments_per_hour:
...     print(exp['hour'], exp['experiments'])
...
2014-06-16 00:00:00+10:00 2
2016-01-01 04:00:00+11:00 1

Fonctions textuelles

Concat

class Concat(*expressions, **extra)[source]

Accepte une liste d’au moins deux champs textes ou expressions et renvoie la concaténation de ces paramètres. Chaque paramètre doit être de type texte ou caractère. Si vous voulez concaténer un champ TextField() avec un champ CharField(), prenez alors la précaution d’indiquer à Django que le résultat output_field sera un champ TextField(). C’est aussi nécessaire lors de la concaténation avec une valeur Value comme dans l’exemple ci-dessous.

Le résultat de cette fonction n’est jamais nul. Pour les moteurs où un paramètre nul aboutit à ce que toute l’expression devienne nulle, Django s’assure que chaque partie nulle est préalablement convertie en chaîne vide.

Exemple d’utilisation :

>>> # Get the display name as "name (goes_by)"
>>> from django.db.models import CharField, Value as V
>>> from django.db.models.functions import Concat
>>> Author.objects.create(name='Margaret Smith', goes_by='Maggie')
>>> author = Author.objects.annotate(
...     screen_name=Concat(
...         'name', V(' ('), 'goes_by', V(')'),
...         output_field=CharField()
...     )
... ).get()
>>> print(author.screen_name)
Margaret Smith (Maggie)

Length

class Length(expression, **extra)[source]

Accepte un champ texte ou une expression unique et renvoie le nombre de caractères de la valeur. Si l’expression est nulle, la longueur renvoyée sera également nulle.

Exemple d’utilisation :

>>> # Get the length of the name and goes_by fields
>>> from django.db.models.functions import Length
>>> Author.objects.create(name='Margaret Smith')
>>> author = Author.objects.annotate(
...    name_length=Length('name'),
...    goes_by_length=Length('goes_by')).get()
>>> print(author.name_length, author.goes_by_length)
(14, None)

Cette expression peut aussi être inscrite comme transformation. Par exemple :

>>> from django.db.models import CharField
>>> from django.db.models.functions import Length
>>> CharField.register_lookup(Length, 'length')
>>> # Get authors whose name is longer than 7 characters
>>> authors = Author.objects.filter(name__length__gt=7)

Lower

class Lower(expression, **extra)[source]

Accepte un champ texte ou une expression unique et renvoie sa représentation en minuscules.

Cette expression peut aussi être inscrite comme transformation comme expliqué pour Length.

Exemple d’utilisation :

>>> from django.db.models.functions import Lower
>>> Author.objects.create(name='Margaret Smith')
>>> author = Author.objects.annotate(name_lower=Lower('name')).get()
>>> print(author.name_lower)
margaret smith

StrIndex

class StrIndex(string, substring, **extra)[source]
New in Django 2.0.

Renvoie un nombre entier positif correspondant à la position indicée (commençant à 1) de la première occurrence de substring dans string, ou 0 si substring est introuvable.

Exemple d’utilisation :

>>> from django.db.models import Value as V
>>> from django.db.models.functions import StrIndex
>>> Author.objects.create(name='Margaret Smith')
>>> Author.objects.create(name='Smith, Margaret')
>>> Author.objects.create(name='Margaret Jackson')
>>> Author.objects.filter(name='Margaret Jackson').annotate(
...     smith_index=StrIndex('name', V('Smith'))
... ).get().smith_index
0
>>> authors = Author.objects.annotate(
...    smith_index=StrIndex('name', V('Smith'))
... ).filter(smith_index__gt=0)
<QuerySet [<Author: Margaret Smith>, <Author: Smith, Margaret>]>

Avertissement

Avec MySQL, la collation d’une table de base de données détermine si les comparaisons de chaînes (telles que dans expression et substring de cette fonction) sont sensibles à la casse. Par défaut, les comparaisons ne sont pas sensibles à la casse.

Substr

class Substr(expression, pos, length=None, **extra)[source]

Renvoie une sous-chaîne de longueur length extraite du champ ou de l’expression à partir de la position pos. L’indice de position commence à 1, il doit donc être plus grand que 0. Si length vaut None, tout le reste de la chaîne est renvoyé comme résultat.

Exemple d’utilisation :

>>> # Set the alias to the first 5 characters of the name as lowercase
>>> from django.db.models.functions import Lower, Substr
>>> Author.objects.create(name='Margaret Smith')
>>> Author.objects.update(alias=Lower(Substr('name', 1, 5)))
1
>>> print(Author.objects.get(name='Margaret Smith').alias)
marga

Upper

class Upper(expression, **extra)[source]

Accepte un champ texte ou une expression unique et renvoie sa représentation en majuscules.

Cette expression peut aussi être inscrite comme transformation comme expliqué pour Length.

Exemple d’utilisation :

>>> from django.db.models.functions import Upper
>>> Author.objects.create(name='Margaret Smith')
>>> author = Author.objects.annotate(name_upper=Upper('name')).get()
>>> print(author.name_upper)
MARGARET SMITH

Fonctions de fenêtrage

New in Django 2.0.

Il existe un certain nombre de fonctions à utiliser dans une expression Window pour calculer la position des éléments ou le Ntile de certaines lignes.

CumeDist

class CumeDist(*expressions, **extra)[source]

Calcule la distribution cumulative d’une valeur à l’intérieur d’une fenêtre ou d’une partition. La distribution cumulative est définie comme le nombre de lignes précédant ou apparentées à la ligne actuelle divisé par le nombre total de lignes dans l’intervalle.

DenseRank

class DenseRank(*expressions, **extra)[source]

Équivalent à Rank mais sans « trous ».

FirstValue

class FirstValue(expression, **extra)[source]

Renvoie la valeur évaluée à la ligne se situant en premier dans l’intervalle de fenêtre, ou None si une telle valeur n’existe pas.

Lag

class Lag(expression, offset=1, default=None, **extra)[source]

Calcule le décalage de valeur selon offset, et si aucune ligne n’existe à cet endroit, renvoie default.

default doit avoir le même type que expression; cependant, cette validation se fait dans la base de données et pas dans le code Python.

LastValue

class LastValue(expression, **extra)[source]

Comparable à FirstValue, renvoie la dernière valeur dans une clause d’intervalle donnée.

Lead

class Lead(expression, offset=1, default=None, **extra)[source]

Calcule la valeur de tête dans un intervalle donné. offset et default sont tous deux évalués en fonction de la ligne actuelle.

default doit avoir le même type que expression; cependant, cette validation se fait dans la base de données et pas dans le code Python.

NthValue

class NthValue(expression, nth=1, **extra)[source]

Calcule la ligne relative au décalage nth (la valeur doit être positive) à l’intérieur de la fenêtre. Renvoie None si aucune ligne ne correspond.

Certaines bases de données gèrent différemment une nième valeur inexistante. Par exemple, Oracle renvoie une chaîne vide au lieu de None pour les expressions basées sur des caractères. Django n’applique aucune conversion dans ces cas.

Ntile

class Ntile(num_buckets=1, **extra)[source]

Calcule une partition pour chacune des lignes dans la clause d’intervalle, distribuant les nombres aussi également que possible entre 1 et num_buckets. Si les lignes ne sont pas divisibles de manière égale par le nombre de « buckets », un ou plusieurs de ceux-ci seront représentés plus fréquemment.

PercentRank

class PercentRank(*expressions, **extra)[source]

Calcule le rang de centile des lignes dans la clause d’intervalle. Ce calcul est équivalent à l’évaluation de

(rank - 1) / (total rows - 1)

Le tableau suivant explique le calcul du rang de centile d’une ligne :

N° de ligne Valeur Rang Calcul Rang en pourcentage
1 15 1 (1-1)/(7-1) 0.0000
2 20 2 (2-1)/(7-1) 0.1666
3 20 2 (2-1)/(7-1) 0.1666
4 20 2 (2-1)/(7-1) 0.1666
5 30 5 (5-1)/(7-1) 0.6666
6 30 5 (5-1)/(7-1) 0.6666
7 40 7 (7-1)/(7-1) 1.0000

Rank

class Rank(*expressions, **extra)[source]

Comparable à RowNumber, cette fonction calcule le rang des lignes de la fenêtre. Le rang calculé comporte des trous. Utilisez DenseRank pour calculer le rang sans trous.

RowNumber

class RowNumber(*expressions, **extra)[source]

Calcule le numéro de ligne selon le tri de la clause d’intervalle ou selon le tri de toute la requête s’il n’y a pas partition dans l”intervalle de fenêtre.

Back to Top