Nouveau dans Django 1.8 Expressions conditionnelles #djangoja

J'ai écrit cet article en participant au Tokyo Django Meetup # 3.

Que pouvez-vous faire?

Nouvelle fonctionnalité ajoutée dans Django 1.8 Expressions conditionnelles, Vous pouvez maintenant passer des expressions conditionnelles aux méthodes ʻannotation, ʻaggregation et ʻupdate` utilisées lors de la manipulation de modèles.

Exemple de modèle

Les modèles suivants sont disponibles. [^ 1]

models.py


# -*- coding: utf-8 -*-
from django.db import models


class Sponsor(models.Model):

    u"""
Sponsor de PyCon JP 2015
    """
    DIAMOND = 'D'
    PLATINUM = 'P'
    GOLD = 'G'
    SILVER = 'S'
    PLAN_CHOICES = (
        (DIAMOND, 'Diamond'),
        (PLATINUM, 'Platinum'),
        (GOLD, 'Gold'),
        (SILVER, 'Silver'),
    )
    name = models.CharField(max_length=50)
    registered_on = models.DateField()
    plan = models.CharField(
        max_length=1,
        choices=PLAN_CHOICES,
        default=SILVER,
    )

Les données de test sont les suivantes.

>>> from datetime import date, timedelta
>>> Sponsor.objects.create(
...     name='Crazy Inc.',
...     plan=Sponsor.DIAMOND,
...     registered_on=date.today() - timedelta(days=19))
<Sponsor: Sponsor object>
>>> Sponsor.objects.create(
...     name='Star Inc.',
...     plan=Sponsor.PLATINUM,
...     registered_on=date.today() - timedelta(days=36))
<Sponsor: Sponsor object>
>>> Sponsor.objects.create(
...     name='Experience Inc.',
...     plan=Sponsor.GOLD,
...     registered_on=date.today() - timedelta(days=11))
<Sponsor: Sponsor object>
>>> Sponsor.objects.create(
...     name='Giorno co., ltd.',
...     plan=Sponsor.GOLD,
...     registered_on=date.today() - timedelta(days=6))
<Sponsor: Sponsor object>
>>> Sponsor.objects.create(
...     name='Chariot Inc.',
...     plan=Sponsor.SILVER,
...     registered_on=date.today() - timedelta(days=1))
<Sponsor: Sponsor object>
>>> Sponsor.objects.create(
...     name='Polnareff co., ltd.',
...     plan=Sponsor.SILVER,
...     registered_on=date.today() - timedelta(days=2))
<Sponsor: Sponsor object>

En fait utiliser

Les expressions conditionnelles sont écrites en combinant les deux classes suivantes.

La classe When spécifie la" condition "en premier et le" résultat "en dernier. La méthode de spécification de la "condition" est la même que la méthode filter.

>>> from django.db.models import When, F, Q
>>> #Reportez-vous au champ de nom lorsque le plan est Gold
>>> When(plan=Sponsor.GOLD, then='name')
>>> When(plan=Sponsor.GOLD, then=F('name'))
>>> from datetime import date
>>> # registered_le est 2015/2/1 à 2015/9/Reportez-vous au champ du plan s'il est compris entre 1
>>> When(registered_on__gt=date(2015, 2, 1),
...      registered_on__lt=date(2015, 9, 1),
...      then='plan')
>>> #Vous pouvez également spécifier des conditions OR dans l'objet Q
>>> When(Q(name__startswith="Crazy") | Q(name__startswith="Giorno"),
...      then='name')

Combinons la classe When ci-dessus avec la classe Case et demandons les frais de parrainage de chaque sponsor.

>>> from django.db.models import Case, When, Value, CharField
>>> Sponsor.objects.annotate(
...     fee=Case(
...         When(plan=Sponsor.DIAMOND, then=Value(u'1,000,000 yens(Hors tax)')),
...         When(plan=Sponsor.PLATINUM, then=Value(u'500,000 yens(Hors tax)')),
...         When(plan=Sponsor.GOLD, then=Value(u'300,000 yens(Hors tax)')),
...         When(plan=Sponsor.SILVER, then=Value(u'100,000 yens(Hors tax)')),
...         output_field=CharField(),
...     ),
... ).values_list('name', 'fee')
[(u'Crazy Inc.', u'1,000,000\u5186 (\u7a0e\u629c\u304d)'), (u'Star Inc.', u'500,000\u5186 (\u7a0e\u629c\u304d)'), (u'Experience Inc.', u'300,000\u5186 (\u7a0e\u629c\u304d)'), (u'Giorno co., ltd.', u'300,000\u5186 (\u7a0e\u629c\u304d)'), (u'Chariot Inc.', u'100,000\u5186 (\u7a0e\u629c\u304d)'), (u'Polnareff co., ltd.', u'100,000\u5186 (\u7a0e\u629c\u304d)')]

Mettons à jour plan en fonction de la valeur de registered_on.

>>> from django.db.models import Case, When, Value
>>> from datetime import date,timedelta
>>> a_week_ago = date.today() - timedelta(weeks=1)
>>> a_month_ago = date.today() - timedelta(days=30)
>>> Sponsor.objects.update(
...     plan=Case(
...         When(registered_on__lte=a_week_ago ,
...              then=Value(Sponsor.PLATINUM)),
...         When(registered_on__lte=a_month_ago,
...              then=Value(Sponsor.GOLD)),
...         default=Value(Sponsor.SILVER)
...     ),
... )
>>> Sponsor.objects.values_list('name', 'plan')
[(u'Crazy Inc.', u'P'), (u'Star Inc.', u'P'), (u'Experience Inc.', u'P'), (u'Giorno co., ltd.', u'S'), (u'Chariot Inc.', u'S'), (u'Polnareff co., ltd.', u'S')]

Découvrez combien d'entreprises ont postulé pour chaque «plan».

>>> from django.db.models import Case, When, Value, IntegerField, Sum
>>> Sponsor.objects.aggregate(
...     silver=Sum(
...         Case(When(plan=Sponsor.SILVER, then=1),
...              output_field=IntegerField())
...     ),
...     gold=Sum(
...         Case(When(plan=Sponsor.GOLD, then=1),
...              output_field=IntegerField())
...     ),
...     platinum=Sum(
...         Case(When(plan=Sponsor.PLATINUM, then=1),
...              output_field=IntegerField())
...     )
... )
{'platinum': 3, 'silver': 3, 'gold': None}

Quel type de SQL est créé?

Jetons un œil au SQL réel créé en utilisant la commande debugsqlshell dans djnago-debug-toolbar. [^ 2]

>>> Sponsor.objects.annotate(
...     fee=Case(
...         When(plan=Sponsor.DIAMOND, then=Value(u'1,000,000 yens(Hors tax)')),
...         When(plan=Sponsor.PLATINUM, then=Value(u'500,000 yens(Hors tax)')),
...         When(plan=Sponsor.GOLD, then=Value(u'300,000 yens(Hors tax)')),
...         When(plan=Sponsor.SILVER, then=Value(u'100,000 yens(Hors tax)')),
...         output_field=CharField(),
...     ),
... ).values_list('name', 'fee')
SELECT `sponsors_sponsor`.`name`,
       CASE
           WHEN `sponsors_sponsor`.`plan` = 'D' THEN '1,000,000 yens(Hors tax)'
           WHEN `sponsors_sponsor`.`plan` = 'P' THEN '500,000 yens(Hors tax)'
           WHEN `sponsors_sponsor`.`plan` = 'G' THEN '300,000 yens(Hors tax)'
           WHEN `sponsors_sponsor`.`plan` = 'S' THEN '100,000 yens(Hors tax)'
           ELSE NULL
       END AS `fee`
FROM `sponsors_sponsor` LIMIT 21 [0.23ms]

>>> Sponsor.objects.update(
...     plan=Case(
...         When(registered_on__lte=a_week_ago ,
...              then=Value(Sponsor.PLATINUM)),
...         When(registered_on__lte=a_month_ago,
...              then=Value(Sponsor.GOLD)),
...         default=Value(Sponsor.SILVER)
...     ),
... )
UPDATE `sponsors_sponsor`
SET `plan` = CASE
                 WHEN `sponsors_sponsor`.`registered_on` <= '2015-04-22' THEN 'P'
                 WHEN `sponsors_sponsor`.`registered_on` <= '2015-03-30' THEN 'G'
                 ELSE 'S'
             END [0.62ms]

>>> Sponsor.objects.aggregate(
...     silver=Sum(
...         Case(When(plan=Sponsor.SILVER, then=1),
...              output_field=IntegerField())
...     ),
...     gold=Sum(
...         Case(When(plan=Sponsor.GOLD, then=1),
...              output_field=IntegerField())
...     ),
...     platinum=Sum(
...         Case(When(plan=Sponsor.PLATINUM, then=1),
...              output_field=IntegerField())
...     )
... )
SELECT SUM(CASE WHEN `sponsors_sponsor`.`plan` = 'P' THEN 1 ELSE NULL END) AS `platinum`,
       SUM(CASE WHEN `sponsors_sponsor`.`plan` = 'S' THEN 1 ELSE NULL END) AS `silver`,
       SUM(CASE WHEN `sponsors_sponsor`.`plan` = 'G' THEN 1 ELSE NULL END) AS `gold`
FROM `sponsors_sponsor` [0.32ms]

[^ 1]: Exemple de code du document original [PyCon JP 2015 Sponsorship Information](https :: Modifié en référence à (/ pycon.jp/2015/sponsors/prospectus.html). [^ 2]: La base de données utilise MySQL 5.5.43.

Recommended Posts

Nouveau dans Django 1.8 Expressions conditionnelles #djangoja
Quoi de neuf dans Python 3.5
Quoi de neuf dans Python 3.6
Nouveautés de Python 3.10 (Résumé)
Nouveau dans Python 3.4.0 (2) --enum
Nouveautés de Python 3.9 (Résumé)
Nouveau dans les dictionnaires de fusion python3.9
Modèle dans Django
Jusqu'à ce que vous créiez une nouvelle application dans Django
Formulaire à Django
Modifications du modèle dans Django
Nouveau dans Python 3.4.0 (1) --pathlib
[Django] Je souhaite me connecter automatiquement après une nouvelle inscription