| 1 |
======================================== |
|---|
| 2 |
Référence de l'API de la base de données |
|---|
| 3 |
======================================== |
|---|
| 4 |
|
|---|
| 5 |
AprÚs avoir créé vos modÚles de données (`data models`_), Django |
|---|
| 6 |
met automatiquement à votre disposition une API d'abstraction de la base de données |
|---|
| 7 |
qui vous permet de créer, récupérer, mettre à jour ou effacer des objets. |
|---|
| 8 |
Ce document explique comment. |
|---|
| 9 |
|
|---|
| 10 |
.. _`data models`: ../model-api/ |
|---|
| 11 |
|
|---|
| 12 |
Dans l'ensemble de ce document, nous ferons référence aux modÚles suivants, qui représentent |
|---|
| 13 |
une application de blog:: |
|---|
| 14 |
|
|---|
| 15 |
class Blog(models.Model): |
|---|
| 16 |
nom = models.CharField(max_length=100) |
|---|
| 17 |
titre = models.TextField() |
|---|
| 18 |
|
|---|
| 19 |
def __unicode__(self): |
|---|
| 20 |
return self.nom |
|---|
| 21 |
|
|---|
| 22 |
class Auteur(models.Model): |
|---|
| 23 |
nom = models.CharField(max_length=50) |
|---|
| 24 |
email = models.EmailField() |
|---|
| 25 |
|
|---|
| 26 |
def __unicode__(self): |
|---|
| 27 |
return self.nom |
|---|
| 28 |
|
|---|
| 29 |
class Billet(models.Model): |
|---|
| 30 |
blog = models.ForeignKey(Blog) |
|---|
| 31 |
titre = models.CharField(max_length=255) |
|---|
| 32 |
texte = models.TextField() |
|---|
| 33 |
date_publication = models.DateTimeField() |
|---|
| 34 |
auteurs = models.ManyToManyField(Author) |
|---|
| 35 |
|
|---|
| 36 |
def __unicode__(self): |
|---|
| 37 |
return self.titre |
|---|
| 38 |
|
|---|
| 39 |
Création d'objets |
|---|
| 40 |
================= |
|---|
| 41 |
|
|---|
| 42 |
Pour représenter les données d'une table de la base de données en objets Python, Django |
|---|
| 43 |
utilise un systÚme intuitif: la classe d'un modÚle représente une table de la |
|---|
| 44 |
base de données, et une instance de cette classe correspond à un enregistrement précis |
|---|
| 45 |
dans cette table. |
|---|
| 46 |
|
|---|
| 47 |
Pour créer un objet, instanciez le en utilisant comme paramÚtres les noms des |
|---|
| 48 |
champs définis dans la classe modÚle, puis appelez ``save()`` pour l'enregistrer |
|---|
| 49 |
dans la base de données. |
|---|
| 50 |
|
|---|
| 51 |
On importe la classe du modÚle d'où qu'il soit dans le path Python, comme vous pouvez |
|---|
| 52 |
l'imaginez. (Cette remarque parce que dans les versions précédentes de Django, |
|---|
| 53 |
l'importation de modÚles était plus complexe.) |
|---|
| 54 |
|
|---|
| 55 |
En supposant que les modÚles se trouvent dans ``mysite/blog/models.py``, voilà un exemple:: |
|---|
| 56 |
|
|---|
| 57 |
from mysite.blog.models import Blog |
|---|
| 58 |
b = Blog(nom='Le blog des Beatles', titre='Toutes les derniÚres nouvelles des Beatles.') |
|---|
| 59 |
b.save() |
|---|
| 60 |
|
|---|
| 61 |
Cela crée, en coulisse, une instruction SQL ``INSERT``. Django |
|---|
| 62 |
ne l'exécute pas tant que vous n'appelez pas explicitement ``save()``. |
|---|
| 63 |
|
|---|
| 64 |
La méthode ``save()`` n'a pas de valeur de retour. |
|---|
| 65 |
|
|---|
| 66 |
Pour créer et sauver un objet en une seule fois, voir la méthode `create`__. |
|---|
| 67 |
|
|---|
| 68 |
__ `create(**kwargs)`_ |
|---|
| 69 |
|
|---|
| 70 |
Incrémentation automatique des clefs primaires |
|---|
| 71 |
---------------------------------------------- |
|---|
| 72 |
|
|---|
| 73 |
Si un modÚle a un champ ``AutoField`` (une clef primaire qui s'incrémente |
|---|
| 74 |
automatiquement) cette valeur incrémentée automatiquement sera calculée et enregistrée |
|---|
| 75 |
comme une propriété de votre objet la premiÚre fois que vous appellerez ``save()``. |
|---|
| 76 |
|
|---|
| 77 |
Exemple:: |
|---|
| 78 |
|
|---|
| 79 |
b2 = Blog(nom='Parlons du Camembert', titre='Réflexions sur les fromages.') |
|---|
| 80 |
b2.id # Renvoie None, car b n'a pas encore d'ID. |
|---|
| 81 |
b2.save() |
|---|
| 82 |
b2.id # Renvoie l'ID de notre nouvel objet. |
|---|
| 83 |
|
|---|
| 84 |
Il n'y a aucun moyen de connaître la valeur d'un champ ID avant d'avoir appelé |
|---|
| 85 |
``save()``, parce que cette valeur est calculée par la base de données, pas par Django. |
|---|
| 86 |
|
|---|
| 87 |
(Pour des raisons pratiques, tous les modÚles ont, par défaut, un champ ``AutoField`` |
|---|
| 88 |
nommé ``id`` à moins qu'on ne spécifie explicitement ``primary_key=True`` sur un |
|---|
| 89 |
autre champ. voir `AutoField documentation`_) |
|---|
| 90 |
|
|---|
| 91 |
.. _AutoField documentation: ../model-api/#autofield |
|---|
| 92 |
|
|---|
| 93 |
Définir explicitement les valeurs d'un champ de clef primaire automatique |
|---|
| 94 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 95 |
|
|---|
| 96 |
Si un modÚle a un champ ``AutoField`` mais que vous souhaitez définir un autre ID |
|---|
| 97 |
pour cet objet, attribuez le explicitement avant de sauver l'objet, plutÃŽt que de |
|---|
| 98 |
compter sur l'attribution automatique de l'ID. |
|---|
| 99 |
|
|---|
| 100 |
Exemple:: |
|---|
| 101 |
|
|---|
| 102 |
b3 = Blog(id=3, nom='Parlons du Camembert', titre='Réflexions sur les fromages.') |
|---|
| 103 |
b3.id # Renvoie 3. |
|---|
| 104 |
b3.save() |
|---|
| 105 |
b3.id # Renvoie 3. |
|---|
| 106 |
|
|---|
| 107 |
Si vous affectez manuellement la valeur d'un champ de clef primaire automatique, |
|---|
| 108 |
faites attention de ne pas utiliser une valeur déjà existante. Si vous utilisez une |
|---|
| 109 |
valeur qui se trouve déjà dans la base de données, Django agira comme si vous |
|---|
| 110 |
mettiez à jour l'objet existant au lieu d'en créer un nouveau. |
|---|
| 111 |
|
|---|
| 112 |
En considérant l'exemple précédent (``'Parlons du Camembert'``), le code ci-dessous remplacera |
|---|
| 113 |
l'entrée dans la base de donnée:: |
|---|
| 114 |
|
|---|
| 115 |
b4 = Blog(id=3, nom='Parlons du Roquefort', titre='Tout sauf du fromage.') |
|---|
| 116 |
b4.save() # Ecrase le blog précédent avec ID=3! |
|---|
| 117 |
|
|---|
| 118 |
Voir `Comment Django sait s'il doit faire un UPDATE ou un INSERT`_, ci-dessous, |
|---|
| 119 |
pour comprendre ce qui se passe. |
|---|
| 120 |
|
|---|
| 121 |
Spécifier explicitement la valeur d'une clef primaire est surtout utile pour |
|---|
| 122 |
enregistrer de gros volumes d'objets, quand on est certain qu'il n'y aura pas de |
|---|
| 123 |
conflit au niveau des clefs primaires. |
|---|
| 124 |
|
|---|
| 125 |
Que se passe-t-il lorsque vous effectuer un enregistrement ? |
|---|
| 126 |
------------------------------------------------------------ |
|---|
| 127 |
|
|---|
| 128 |
Lorsque que l'on enregistre un objet, Django effectue les étapes suivantes: |
|---|
| 129 |
|
|---|
| 130 |
1. **Ãmission d'un signal de pré-enregistrement (``pre_save``).** Indique que |
|---|
| 131 |
l'objet va bientÎt être enregistré. On peut mentionner un listener qui sera |
|---|
| 132 |
appelé à chaque émission d'un signal. (Il n'existe pas encore de documents |
|---|
| 133 |
sur ces signaux.) |
|---|
| 134 |
|
|---|
| 135 |
2. **Prétraitement des données.** Chaque champ de l'objet reçoit l'ordre d'effectuer |
|---|
| 136 |
une modification automatique des données si nécessaire. |
|---|
| 137 |
|
|---|
| 138 |
La plupart des champs ne font *aucun* prétraitement. Le champ de données |
|---|
| 139 |
est conservé tel quel. Le prétraitement est réservé aux champs ayant un comportement |
|---|
| 140 |
spécifique. Par exemple, si votre modÚle à un champ ``DateField`` avec l'attribut |
|---|
| 141 |
``auto_now=True``, la phase de préparation à la sauvegarde modifiera les données |
|---|
| 142 |
de l'objet pour s'assurer que le champ de date contient la date du jour. |
|---|
| 143 |
(Notre documentation n'inclut pas encore une liste de tous les champs ayant |
|---|
| 144 |
un "comportement spécifique".) |
|---|
| 145 |
|
|---|
| 146 |
3. **Préparation des données pour la base de données.** Chaque champ reçoit l'ordre |
|---|
| 147 |
de fournir sa valeur actuelle dans un type de données qui puisse être écrit dans |
|---|
| 148 |
la base de données. |
|---|
| 149 |
|
|---|
| 150 |
La plupart des champs *ne* nécessitent *aucune* préparation. Les types de données |
|---|
| 151 |
simples, comme les entiers ou les chaînes de caractÚres, sont 'prêts pour l'écriture' |
|---|
| 152 |
en tant qu'objet Python. Cependant, les types de données plus complexes nécessitent |
|---|
| 153 |
souvent quelques modifications. |
|---|
| 154 |
|
|---|
| 155 |
Par exemple, les champs ``DateField`` utilisent un objet Python ``datetime`` pour |
|---|
| 156 |
stocker les données. Les bases de données ne pouvant pas stocker des objets |
|---|
| 157 |
``datetime``, la valeur du champ doit être convertie en une chaîne de caractÚres |
|---|
| 158 |
conforme à l'ISO pour pouvoir être insérée dans la base de données. |
|---|
| 159 |
|
|---|
| 160 |
4. **Insertion des données dans la base de données.** Les données prétraitées et préparées |
|---|
| 161 |
sont ensuite transformées en une requête SQL d'insertion dans la base de données. |
|---|
| 162 |
|
|---|
| 163 |
5. **Ãmission d'un signal de post-enregistrement (``post_save``).** Tout comme le signal |
|---|
| 164 |
de pré-enregistrement, il sert à indiquer que l'objet a été enregistré correctement. |
|---|
| 165 |
|
|---|
| 166 |
Enregistrer les modifications des objets |
|---|
| 167 |
======================================== |
|---|
| 168 |
|
|---|
| 169 |
Pour enregistrer les changements d'un objet qui existe déjà dans la base de données, |
|---|
| 170 |
utilisez ``save()``. |
|---|
| 171 |
|
|---|
| 172 |
Soit une instance ``b5`` de la classe ``Blog`` qui existe déjà dans la base de données, |
|---|
| 173 |
dans cet exemple on change son nom et on met à jour l'objet dans la base de données:: |
|---|
| 174 |
|
|---|
| 175 |
b5.nom = 'Nouveau nom' |
|---|
| 176 |
b5.save() |
|---|
| 177 |
|
|---|
| 178 |
Cela crée, en coulisse, une instruction SQL ``UPDATE``. Django ne l'exécute pas |
|---|
| 179 |
tant que vous n'appelez pas explicitement ``save()``. |
|---|
| 180 |
|
|---|
| 181 |
La méthode ``save()`` n'a pas de valeur de retour. |
|---|
| 182 |
|
|---|
| 183 |
Enregistrement des champs ForeignKey et ManyToManyField |
|---|
| 184 |
------------------------------------------------------- |
|---|
| 185 |
|
|---|
| 186 |
La mise à jour d'un champ ``ForeignKey`` fonctionne exactement de la même maniÚre |
|---|
| 187 |
que l'enregistrement d'un champ simple. Il suffit d'associer un objet du bon type |
|---|
| 188 |
au champ en question:: |
|---|
| 189 |
|
|---|
| 190 |
blog_fromage = Blog.objects.get(nom="Parlons du Camembert") |
|---|
| 191 |
billet.blog = blog_fromage |
|---|
| 192 |
billet.save() |
|---|
| 193 |
|
|---|
| 194 |
La mise à jour d'un champ ``ManyToManyField`` fonctionne un peu différemment. On |
|---|
| 195 |
utilise la méthode ``add()`` sur le champ pour ajouter un enregistrement à la |
|---|
| 196 |
relation:: |
|---|
| 197 |
|
|---|
| 198 |
joe = Author.objects.create(nom="Joe") |
|---|
| 199 |
Billet.authors.add(joe) |
|---|
| 200 |
|
|---|
| 201 |
Django fera la grimace si vous essayez d'affecter ou d'ajouter un objet du mauvais type. |
|---|
| 202 |
|
|---|
| 203 |
Comment Django sait s'il doit faire un UPDATE ou un INSERT |
|---|
| 204 |
---------------------------------------------------------- |
|---|
| 205 |
|
|---|
| 206 |
Vous avez peut-être remarqué qu'on utilise la méthode ``save()`` que ce soit pour créer |
|---|
| 207 |
ou pour modifier des objets. Avec Django, il n'est pas nécessaire d'utiliser une |
|---|
| 208 |
instruction SQL ``INSERT`` ou ``UPDATE``. Pour être précis, lorsqu'on appelle la |
|---|
| 209 |
méthode ``save()``, il utilise l'algorithme suivant: |
|---|
| 210 |
|
|---|
| 211 |
* Si la clef primaire de l'objet possÚde une valeur que l'on peut estimer à ``True`` |
|---|
| 212 |
(c'est-à -dire qui ne soit ni ``None`` ni une chaîne vide), Django exécute une |
|---|
| 213 |
requête ``SELECT`` pour vérifier s'il existe un enregistrement avec la même |
|---|
| 214 |
clef primaire. |
|---|
| 215 |
* S'il en trouve un, Django exécute alors un ``UPDATE`` pour le |
|---|
| 216 |
mettre à jour. |
|---|
| 217 |
* Si la clef primaire de l'objet n'est *pas* spécifiée, ou qu'elle l'est mais |
|---|
| 218 |
qu'elle n'existe pas dans la table, Django exécute un ``INSERT``. |
|---|
| 219 |
|
|---|
| 220 |
Le point à retenir est qu'il ne faut pas définir explicitement une valeur à la clef primaire |
|---|
| 221 |
quand on enregistre de nouveaux objets si on n'est pas certain que cette valeur n'est pas |
|---|
| 222 |
déjà utilisée. Pour plus de détails à ce propos, reportez-vous au paragraphe "Définir |
|---|
| 223 |
explicitement la valeur d'un champ de clef primaire automatique" ci-dessus. |
|---|
| 224 |
|
|---|
| 225 |
Récupération des objets |
|---|
| 226 |
======================= |
|---|
| 227 |
|
|---|
| 228 |
Pour récupérer des objets de votre base de données, il vous faut construire un |
|---|
| 229 |
``QuerySet`` via un ``Manager`` sur la classe de votre modÚle. |
|---|
| 230 |
|
|---|
| 231 |
Un ``QuerySet`` représente un groupe d'objets de votre base de données. Il |
|---|
| 232 |
peut être constitué d'aucun, d'un seul ou de plusieurs *filtres* qui affinent la |
|---|
| 233 |
sélection d'objets selon les paramÚtres transmis. En SQL, un ``QuerySet`` équivaut |
|---|
| 234 |
à un ``SELECT``, et un filtre à une clause comme ``WHERE`` ou ``LIMIT``. |
|---|
| 235 |
|
|---|
| 236 |
On obtient un ``QuerySet`` en utilisant le ``Manager`` du modÚle. Chaque modÚle |
|---|
| 237 |
a au moins un ``Manager`` nommé, par défaut, ``objects``. On y accÚde directement |
|---|
| 238 |
via la classe du modÚle comme suit:: |
|---|
| 239 |
|
|---|
| 240 |
Blog.objects # <django.db.models.manager.Manager object at ...> |
|---|
| 241 |
b = Blog(nom='Foo', titre='Bar') |
|---|
| 242 |
b.objects # AttributeError: "Manager isn't accessible via Blog instances." |
|---|
| 243 |
|
|---|
| 244 |
(Les ``Managers`` ne sont accessibles que par la classe du modÚle (méthode de classe), |
|---|
| 245 |
et non pas par une de ses instances. Et cela pour bien signifier la séparation entre |
|---|
| 246 |
les opérations au niveau table et les opérations au niveau enregistrement) |
|---|
| 247 |
|
|---|
| 248 |
Le ``Manager`` est la source principale de ``QuerySets`` d'un modÚle. Il |
|---|
| 249 |
fonctionne comme un ``QuerySet`` racine qui décrit tous les objets d'un modÚle |
|---|
| 250 |
qui sont dans la base de données. Par exemple, ``Blog.objects`` est le ``QuerySet`` |
|---|
| 251 |
de base qui contient tous les objets ``Blog`` qui sont dans la base de données. |
|---|
| 252 |
|
|---|
| 253 |
Récupération tous les objets |
|---|
| 254 |
---------------------------- |
|---|
| 255 |
|
|---|
| 256 |
La maniÚre la plus simple de récupérer les objets d'une table est de les prendre |
|---|
| 257 |
tous. Pour ce faire on utilise la méthode ``all()`` sur un ``Manager``. |
|---|
| 258 |
|
|---|
| 259 |
Exemple:: |
|---|
| 260 |
|
|---|
| 261 |
tout_les_billets = Billet.objects.all() |
|---|
| 262 |
|
|---|
| 263 |
La méthode ``all()`` renvoie un ``QuerySet`` de tous les objets de la base. |
|---|
| 264 |
|
|---|
| 265 |
(Si ``Billet.objects`` est un ``QuerySet``, pourquoi ne peut-on pas simplement |
|---|
| 266 |
utiliser ``Billet.objects`` ? Parce que ``Billet.objects`` est le ``QuerySet`` racine; |
|---|
| 267 |
c'est un cas particulier, il ne peut pas être évalué. La méthode ``all()``, elle, |
|---|
| 268 |
renvoie un ``QuerySet`` qui *peut* être évalué.) |
|---|
| 269 |
|
|---|
| 270 |
Filtrage des objets |
|---|
| 271 |
------------------- |
|---|
| 272 |
|
|---|
| 273 |
Le ``QuerySet`` racine fourni par le ``Manager`` décrit tous les objets de la |
|---|
| 274 |
base. Mais en général on a plutÎt besoin d'accéder à une sélection particuliÚre |
|---|
| 275 |
d'objets plutÃŽt qu'Ã tous. |
|---|
| 276 |
|
|---|
| 277 |
Pour créer une sélection, on affine le ``QuerySet`` de base en lui ajoutant |
|---|
| 278 |
des filtres. Les deux façons les plus simples sont les suivantes: |
|---|
| 279 |
|
|---|
| 280 |
``filter(**kwargs)`` |
|---|
| 281 |
Renvoie un nouveau ``QuerySet`` contenant les objets qui correspondent aux |
|---|
| 282 |
critÚres de recherche fournis. |
|---|
| 283 |
|
|---|
| 284 |
``exclude(**kwargs)`` |
|---|
| 285 |
Renvoie un nouveau ``QuerySet`` contenant les objets qui *ne* correspondent *pas* |
|---|
| 286 |
aux critÚres de recherche. |
|---|
| 287 |
|
|---|
| 288 |
Les paramÚtres de recherche, (``**kwargs`` dans l'exemple précédent) doivent être |
|---|
| 289 |
dans le format `Recherche sur un champ`_ décrit ci-dessous. |
|---|
| 290 |
|
|---|
| 291 |
Par exemple pour obtenir un ``QuerySet`` de tous les billets de l'année 2006, |
|---|
| 292 |
on utilise ``filter()`` de cette façon:: |
|---|
| 293 |
|
|---|
| 294 |
Billet.objects.filter(date_publication__year=2006) |
|---|
| 295 |
|
|---|
| 296 |
(Notez que l'on a pas à utiliser ``all()`` -- ``Billet.objects.all().filter(...)``. |
|---|
| 297 |
Cela fonctionnerait quand même, mais c'est inutile. On utilise ``all()`` seulement quand on |
|---|
| 298 |
veut tous les objets.) |
|---|
| 299 |
|
|---|
| 300 |
Chaînage des filtres |
|---|
| 301 |
~~~~~~~~~~~~~~~~~~~~ |
|---|
| 302 |
|
|---|
| 303 |
Le résultat de l'affinage d'un ``QuerySet`` est un autre ``QuerySet``. |
|---|
| 304 |
Il est donc possible d'enchaîner les filtres. Par exemple:: |
|---|
| 305 |
|
|---|
| 306 |
Billet.objects.filter( |
|---|
| 307 |
titre__startswith='Quel').exclude( |
|---|
| 308 |
date_publication__gte=datetime.now()).filter( |
|---|
| 309 |
date_publication__gte=datetime(2005, 1, 1)) |
|---|
| 310 |
|
|---|
| 311 |
...utilise le ``QuerySet`` de base de tous les enregistrements de la table, y ajoute |
|---|
| 312 |
un filtre, puis une exclusion et enfin un autre filtre. Le résultat est un |
|---|
| 313 |
``QuerySet`` contenant tous les billets dont le titre commence par |
|---|
| 314 |
"Quel", publiés entre le 1er janvier 2005 et aujourd'hui. |
|---|
| 315 |
|
|---|
| 316 |
Chaque ``QuerySet`` filtré est unique |
|---|
| 317 |
------------------------------------- |
|---|
| 318 |
A chaque fois qu'on affine un ``QuerySet``, on obtient un |
|---|
| 319 |
``QuerySet`` tout neuf aucunement lié au précédent. Chaque affinage |
|---|
| 320 |
crée donc un ``QuerySet`` bien distinct, qui peut être stocké, |
|---|
| 321 |
utilisé et réutilisé. |
|---|
| 322 |
|
|---|
| 323 |
Exemple:: |
|---|
| 324 |
|
|---|
| 325 |
q1 = Billet.objects.filter(titre__startswith="Quel") |
|---|
| 326 |
q2 = q1.exclude(date_publication__gte=datetime.now()) |
|---|
| 327 |
q3 = q1.filter(date_publication__gte=datetime.now()) |
|---|
| 328 |
|
|---|
| 329 |
Ces trois ``QuerySets`` sont distincts. Le premier est un ``QuerySet`` de base |
|---|
| 330 |
contenant tous les billets dont le titre commence par "Quel". Le second |
|---|
| 331 |
est un sous groupe du premier. Le critÚre supplémentaire exclut les billets |
|---|
| 332 |
dont le champ ``date_publication`` est supérieur à la date d'aujourd'hui. Le troisiÚme |
|---|
| 333 |
est lui aussi un sous groupe du premier, qui contient uniquement les billets |
|---|
| 334 |
dont le champ ``date_publication`` est supérieur à la date d'aujourd'hui. Le ``QuerySet`` |
|---|
| 335 |
de base (``q1``) n'est aucunement affecté par ce processus de filtrage. |
|---|
| 336 |
|
|---|
| 337 |
Les ``QuerySets`` sont passifs |
|---|
| 338 |
------------------------------ |
|---|
| 339 |
|
|---|
| 340 |
Les ``QuerySets`` sont passifs, parce que leur création n'implique aucun appel |
|---|
| 341 |
à la base de données. On peut empiler des filtres toute la journée, Django n'exécutera |
|---|
| 342 |
vraiment la requête que lorsque le ``QuerySet`` sera *évalué*. |
|---|
| 343 |
|
|---|
| 344 |
Quand les ``QuerySets`` sont-ils évalués ? |
|---|
| 345 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 346 |
|
|---|
| 347 |
On peut évaluer un ``QuerySet`` par les façons suivants: |
|---|
| 348 |
|
|---|
| 349 |
* **Itération.** Un ``QuerySet`` est un itérateur, et il exécute sa requête |
|---|
| 350 |
sur la base de données la premiÚre fois qu'on lance une itération dessus. |
|---|
| 351 |
Par exemple, pour afficher les titres de toutes les billets de la base de |
|---|
| 352 |
données, on aura:: |
|---|
| 353 |
|
|---|
| 354 |
for b in Billet.objects.all(): |
|---|
| 355 |
print b.titre |
|---|
| 356 |
|
|---|
| 357 |
* **Découpage.** Comme on l'explique ci-dessous dans le paragraphe `Limiter un |
|---|
| 358 |
QuerySet`_, un ``QuerySet`` peut être découpé en utilisant la syntaxe |
|---|
| 359 |
Python pour le découpage de liste. En général découper un ``QuerySet`` |
|---|
| 360 |
renvoie un autre ``QuerySet`` (non évalué), mais Django exécutera une |
|---|
| 361 |
requête sur la base de données si vous utilisez le paramÚtre "pas" de la |
|---|
| 362 |
syntaxe de découpage. |
|---|
| 363 |
|
|---|
| 364 |
* **repr().** Un ``QuerySet`` est évalué quand on le passe à la fonction |
|---|
| 365 |
``repr()``. Ceci est pratique quand on utilise l'API dans l'interpréteur |
|---|
| 366 |
interactif car on voit tout de suite les résultats. |
|---|
| 367 |
|
|---|
| 368 |
* **len().** Un ``QuerySet`` est évalué quand on le passe à la fonction |
|---|
| 369 |
len(). Comme on peut s'en douter, cela renvoie la longueur du ``QuerySet``. |
|---|
| 370 |
|
|---|
| 371 |
Note: *N'utilisez pas* len() si vous voulez juste savoir le nombre |
|---|
| 372 |
d'éléments contenus dans un ``QuerySet``. Il est beaucoup plus efficace |
|---|
| 373 |
de demander à la base de données de les compter, en utilisant une |
|---|
| 374 |
instruction SQL ``SELECT COUNT(*)``. Django propose précisément une méthode |
|---|
| 375 |
``count()`` pour ça. Voir ``count()`` plus bas. |
|---|
| 376 |
|
|---|
| 377 |
* **list().** On peut forcer l'évaluation d'un ``QuerySet`` en utilisant la |
|---|
| 378 |
fonction ``list()``. Par exemple:: |
|---|
| 379 |
|
|---|
| 380 |
liste_billet = list(Billet.objects.all()) |
|---|
| 381 |
|
|---|
| 382 |
Attention quand même car cela peut entraîner une trÚs grosse consommation de |
|---|
| 383 |
mémoire parce que Django chargera chaque élément de la liste en mémoire. |
|---|
| 384 |
A l'opposé, lorsque que l'on parcourt un ``QuerySet`` par itération, on |
|---|
| 385 |
profite de la capacité des bases de données à charger les données et générer |
|---|
| 386 |
les objets au fur et à mesure qu'on en a besoin. |
|---|
| 387 |
|
|---|
| 388 |
Sérialisation de ``QuerySet`` |
|---|
| 389 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 390 |
|
|---|
| 391 |
Si on sérialise_ un ``QuerySet``, cela forcera le chargement de tous les |
|---|
| 392 |
résultats en mémoire avant la sérialisation. La sérialisation est généralement |
|---|
| 393 |
utilisée en tant que précurseur pour la sauvegarde et lorsque le ``QuerySet`` sauvé |
|---|
| 394 |
est restauré, on désire que les résultats soient aussi présents. Cela signifie |
|---|
| 395 |
que lorsque l'on désérialise un ``QuerySet``, cela contient les résultats au moment |
|---|
| 396 |
de la sérialisation, et non les résultats actuels de la base de données. |
|---|
| 397 |
|
|---|
| 398 |
Si l'on veut uniquement sérialiser les informations nécessaires pour récréer |
|---|
| 399 |
le ``QuerySet`` ultérieurement à partir de la base de données, il suffit de sérialiser |
|---|
| 400 |
l'attribut ``query`` du ``QuerySet``. On peut alors récréer le ``QuerySet`` original |
|---|
| 401 |
(sans aucun résultat chargé) en utilisant un code comme celui-ci:: |
|---|
| 402 |
|
|---|
| 403 |
>>> import pickle |
|---|
| 404 |
>>> requete = pickle.loads(s) # Supposons que 's' est la chaîne de sérialisation. |
|---|
| 405 |
>>> qs = MonModele.objects.all() |
|---|
| 406 |
>>> qs.query = requete # Restaure la requête original. |
|---|
| 407 |
|
|---|
| 408 |
.. _sérialise: http://docs.python.org/lib/module-pickle.html |
|---|
| 409 |
|
|---|
| 410 |
Limiter un QuerySet |
|---|
| 411 |
------------------- |
|---|
| 412 |
|
|---|
| 413 |
On utilise la syntaxe de découpage de liste de Python pour limiter |
|---|
| 414 |
un ``QuerySet`` à un certain nombre de résultats. C'est l'équivalent des |
|---|
| 415 |
clauses ``LIMIT`` et ``OFFSET`` en SQL. |
|---|
| 416 |
|
|---|
| 417 |
Par exemple, pour renvoyer les 5 premiers objets (``LIMIT 5``), on aura:: |
|---|
| 418 |
|
|---|
| 419 |
Billet.objects.all()[:5] |
|---|
| 420 |
|
|---|
| 421 |
Pour renvoyer cinq objets, du sixiÚme au dixiÚme (``OFFSET 5 LIMIT 5``), on aura:: |
|---|
| 422 |
|
|---|
| 423 |
Billet.objects.all()[5:10] |
|---|
| 424 |
|
|---|
| 425 |
On peut aussi découper à partir de l'élément 'N' jusqu'à la fin du ``QuerySet``. |
|---|
| 426 |
Par exemple, pour renvoyer tous les éléments à partir du sixiÚme, on aura:: |
|---|
| 427 |
|
|---|
| 428 |
Billet.objects.all()[5:] |
|---|
| 429 |
|
|---|
| 430 |
L'implémentation de l'exemple précédent en SQL varie selon la base de données |
|---|
| 431 |
utilisée, mais dans tous les cas, cette syntaxe est supportée. |
|---|
| 432 |
|
|---|
| 433 |
En général, le découpage d'un ``QuerySet`` renvoie un nouveau ``QuerySet`` sans |
|---|
| 434 |
évaluation de la requête. Ãa n'est pas le cas lorsqu'on utilise le paramÚtre |
|---|
| 435 |
"step" de la syntaxe de découpage de Python. Dans l'exemple suivant, la requête |
|---|
| 436 |
sera exécutée pour obtenir un objet sur deux dans l'intervalle allant du 1er au |
|---|
| 437 |
10Úme:: |
|---|
| 438 |
|
|---|
| 439 |
Billet.objects.all()[:10:2] |
|---|
| 440 |
|
|---|
| 441 |
Pour récupérer un seul objet, plutÎt qu'une liste (``SELECT foo FROM bar LIMIT 1``), |
|---|
| 442 |
utilisez un simple index plutÃŽt qu'un intervalle. L'exemple ci-dessous renvoie le premier |
|---|
| 443 |
objet, aprÚs les avoir mis dans l'ordre alphabétique en fonction de leur titre:: |
|---|
| 444 |
|
|---|
| 445 |
Billet.objects.order_by('titre')[0] |
|---|
| 446 |
|
|---|
| 447 |
Le résultat sera à peu prÚs le même avec l'instruction suivante:: |
|---|
| 448 |
|
|---|
| 449 |
Billet.objects.order_by('titre')[0:1].get() |
|---|
| 450 |
|
|---|
| 451 |
A noter cependant que la premiÚre syntaxe lÚvera une exception ``IndexError`` si |
|---|
| 452 |
aucun objet ne correspond aux critÚres, alors que la seconde lÚvera une |
|---|
| 453 |
exception ``DoesNotExist``. |
|---|
| 454 |
|
|---|
| 455 |
Les méthodes des ``QuerySets`` qui renvoient d'autres ``QuerySets`` |
|---|
| 456 |
------------------------------------------------------------------- |
|---|
| 457 |
|
|---|
| 458 |
Django fournit une série de méthodes d'affinage des ``QuerySets``, qui modifient soit |
|---|
| 459 |
le type de résultat renvoyé soit la maniÚre dont la requête SQL est exécutée. |
|---|
| 460 |
|
|---|
| 461 |
``filter(**kwargs)`` |
|---|
| 462 |
~~~~~~~~~~~~~~~~~~~~ |
|---|
| 463 |
|
|---|
| 464 |
Renvoie un nouveau ``QuerySet`` contenant les objets qui correspondent |
|---|
| 465 |
aux critÚres de recherche. |
|---|
| 466 |
|
|---|
| 467 |
Les paramÚtres de recherche (``**kwargs``) doivent être dans le format décrit |
|---|
| 468 |
dans la rubrique `Recherche sur un champ`_ plus bas. Si il y a plusieurs paramÚtres, ils |
|---|
| 469 |
sont ajoutés en utilisant ``AND`` dans l'instruction SQL sous-jacente. |
|---|
| 470 |
|
|---|
| 471 |
``exclude(**kwargs)`` |
|---|
| 472 |
~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 473 |
|
|---|
| 474 |
Renvoie un nouveau ``QuerySet`` contenant tous les objets qui ne correspondent |
|---|
| 475 |
*pas* aux critÚres de recherche. |
|---|
| 476 |
|
|---|
| 477 |
Les paramÚtres de recherche (``**kwargs``) doivent être dans le format décrit |
|---|
| 478 |
dans la rubrique `Recherche sur un champ`_. Si il y a plusieurs paramÚtres, |
|---|
| 479 |
ils sont joints dans la requête SQL par un ``AND`` et contenus dans un ``NOT()``. |
|---|
| 480 |
|
|---|
| 481 |
Dans cet exemple, tous les enregistrements dont ``date_publication`` est postérieur au |
|---|
| 482 |
3/1/2005 ET ceux dont le champ ``titre`` est "bonjour":: |
|---|
| 483 |
|
|---|
| 484 |
Billet.objects.exclude(date_publication__gt=datetime.date(2005, 1, 3), titre='bonjour') |
|---|
| 485 |
|
|---|
| 486 |
La requête SQL correspondante est:: |
|---|
| 487 |
|
|---|
| 488 |
SELECT ... |
|---|
| 489 |
WHERE NOT (date_publication > '2005-1-3' AND titre = 'Bonjour') |
|---|
| 490 |
|
|---|
| 491 |
Dans ce deuxiÚme exemple, tous les enregistrements dont ``date_publication`` est |
|---|
| 492 |
postérieur au 3/1/2005 OU ceux dont le champ ``titre`` est "bonjour":: |
|---|
| 493 |
|
|---|
| 494 |
Billet.objects.exclude(date_publication__gt=datetime.date(2005, 1, 3)).exclude(titre='bonjour') |
|---|
| 495 |
|
|---|
| 496 |
La requête SQL correspondante est:: |
|---|
| 497 |
|
|---|
| 498 |
SELECT ... |
|---|
| 499 |
WHERE NOT date_publication > '2005-1-3' |
|---|
| 500 |
AND NOT titre = 'bonjour' |
|---|
| 501 |
|
|---|
| 502 |
Ce dernier exemple est donc plus restrictif. |
|---|
| 503 |
|
|---|
| 504 |
``order_by(*fields)`` |
|---|
| 505 |
~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 506 |
|
|---|
| 507 |
Par défaut, les résultats renvoyés par un ``QuerySet`` sont classés d'aprÚs le tuple |
|---|
| 508 |
de classement du paramÚtre ``ordering`` de la classe ``Meta`` du modÚle. |
|---|
| 509 |
On peut redéfinir cet ordre au niveau de chaque ``QuerySet`` en utilisant sa |
|---|
| 510 |
méthode ``order_by``. |
|---|
| 511 |
|
|---|
| 512 |
Exemple:: |
|---|
| 513 |
|
|---|
| 514 |
Billet.objects.filter(date_publication__year=2005).order_by('-date_publication', 'titre') |
|---|
| 515 |
|
|---|
| 516 |
Le résultat de ce ``QuerySet`` sera classé en ordre décroissant du champ |
|---|
| 517 |
``date_publication``, puis en ordre croissant du champ ``titre``. |
|---|
| 518 |
Le signe "moins" dans ``"-date_publication"`` indique l'ordre décroissant. L'ordre croissant |
|---|
| 519 |
est implicite. Pour obtenir un ordre aléatoire, utilisez ``"?"``:: |
|---|
| 520 |
|
|---|
| 521 |
Billet.objects.order_by('?') |
|---|
| 522 |
|
|---|
| 523 |
Note: Les requêtes ``order_by('?')`` peuvent être coûteuses et lentes selon |
|---|
| 524 |
la base de données utilisée. |
|---|
| 525 |
|
|---|
| 526 |
Pour classer sur un champ se trouvant dans une autre table, on utilise |
|---|
| 527 |
la même syntaxe qu'avec les requêtes sur les relations des modÚles. C'est-à -dire, |
|---|
| 528 |
le nom du champ, suivi par double soulignement (``__``), suivi par le nom du champ |
|---|
| 529 |
dans le nouveau modÚle, et ainsi de suite jusqu'au modÚle désiré. Par exemple:: |
|---|
| 530 |
|
|---|
| 531 |
Billet.objects.order_by('blog__nom', 'titre') |
|---|
| 532 |
|
|---|
| 533 |
Si on essaye de classer par un champ qui est une relation vers un autre modÚle, Django |
|---|
| 534 |
utilisera l'ordre par défaut défini dans le modÚle relié (ou trier par la clef primaire |
|---|
| 535 |
du modÚle relié s'il n'y a pas de ``Meta.ordering`` spécifié). Par exemple:: |
|---|
| 536 |
|
|---|
| 537 |
Billet.objects.order_by('blog') |
|---|
| 538 |
|
|---|
| 539 |
...est identique à :: |
|---|
| 540 |
|
|---|
| 541 |
Billet.objects.order_by('blog__id') |
|---|
| 542 |
|
|---|
| 543 |
...puisque le modÚle ``Blog`` n'a pas d'ordre prédéfini. |
|---|
| 544 |
|
|---|
| 545 |
Il faut faire attention à l'utilisation du tri par un champ sur un objet relié |
|---|
| 546 |
lorsqu'on utilise aussi ``distinct()``. Voir la note dans la section `distinct()`_ |
|---|
| 547 |
pour comprendre comment le tri par objet relié peut modifié les résultats attendus. |
|---|
| 548 |
|
|---|
| 549 |
Il est toléré de spécifier un champ multi-valué pour trier les résultats (par exemple, |
|---|
| 550 |
un champ ``ManyToMany``). Normalement, cela n'est pas une chose sensé et cela est vraiment |
|---|
| 551 |
une fonctionnalité pour un usage avancé. Cependant, si vous savez que les données filtrées |
|---|
| 552 |
de votre ``QuerySet`` ou les données disponibles implique qu'il n'y aura qu'un tri possible |
|---|
| 553 |
pour chaque élément sélectionné, ce tri peut-être exactement ce que vous cherchiez. Utilisez |
|---|
| 554 |
le tri sur champ multi-valué avec précaution et vérifié que les résultats obtenus sont ceux |
|---|
| 555 |
désirés. |
|---|
| 556 |
|
|---|
| 557 |
**Nouveau dans la version de développement:** Si on ne souhaite qu'aucun ordre ne soit appliqué |
|---|
| 558 |
à la requête, pas même l'ordre par défaut, on appelle ``order_by()`` sans paramÚtres. |
|---|
| 559 |
|
|---|
| 560 |
**Nouveau dans la version de développement:** La syntaxe pour trier grâce aux objets reliés |
|---|
| 561 |
à changer. Voir la `documentation Django 0.96`_ pour l'ancienne syntaxe. |
|---|
| 562 |
|
|---|
| 563 |
.. _documentation Django 0.96: http://www.djangoproject.com/documentation/0.96/model-api/#floatfield |
|---|
| 564 |
|
|---|
| 565 |
On ne peut pas préciser si le tri doit être sensible à la casse, Django |
|---|
| 566 |
renverra le résultat tel qu'il est renvoyé par votre base de données. |
|---|
| 567 |
|
|---|
| 568 |
``reverse()`` |
|---|
| 569 |
~~~~~~~~~~~~~ |
|---|
| 570 |
|
|---|
| 571 |
**Nouveau dans la version de développement** |
|---|
| 572 |
|
|---|
| 573 |
On utilise la méthode ``reverse()`` pour inverser l'ordre dans lequel ont |
|---|
| 574 |
été renvoyé les éléments du ``QuerySet``. L'appel à ``reverse()`` une |
|---|
| 575 |
seconde fois, restaure l'ordre initial. |
|---|
| 576 |
|
|---|
| 577 |
Pour retrouver les cinq ''derniers'' éléments d'un ``QuerySet``, on peut utiliser ceci:: |
|---|
| 578 |
|
|---|
| 579 |
mon_queryset.reverse()[:5] |
|---|
| 580 |
|
|---|
| 581 |
Notez que cela n'est pas exactement la même chose que le découpage à partir |
|---|
| 582 |
de la fin d'une séquence en Python. L'exemple précédent retournera le dernier |
|---|
| 583 |
élément en premier, puis le pénultiÚme et ainsi de suite. S'il s'agissait d'une |
|---|
| 584 |
séquence Python et que nous regardions ``seq[:-5]``, le premier élément serait |
|---|
| 585 |
le cinquiÚme en partant de la fin. Django ne supporte pas ce mode d'accÚs |
|---|
| 586 |
(découpage à partir de la fin), car il est impossible de la faire efficacement |
|---|
| 587 |
en SQL. |
|---|
| 588 |
|
|---|
| 589 |
``distinct()`` |
|---|
| 590 |
~~~~~~~~~~~~~~ |
|---|
| 591 |
|
|---|
| 592 |
Renvoie un nouveau ``QuerySet`` dont la requête SQL utilise ``SELECT DISTINCT``. |
|---|
| 593 |
Cela permet d'éliminer les doublons. |
|---|
| 594 |
|
|---|
| 595 |
Par défaut, un ``QuerySet`` n'élimine pas les doublons. En pratique ça n'est |
|---|
| 596 |
généralement pas un problÚme parce que, avec des requêtes simple comme ``Blog.objects.all()`` |
|---|
| 597 |
, il n'y a pas possibilité de doublons. |
|---|
| 598 |
Par contre, lorsqu'on fait une requête sur plusieurs tables, cela peut arriver, |
|---|
| 599 |
rendant utile l'usage de la méthode ``distinct()``. |
|---|
| 600 |
|
|---|
| 601 |
.. note:: |
|---|
| 602 |
Chaque champ utilisé lors d'un appel à ``ordre_by()`` est inclut |
|---|
| 603 |
dans la colonne ``SELECT``. Cela peut parfois mener à des résultats |
|---|
| 604 |
inattendus lorsqu'on l'utilise en conjonction avec ``distinct()``. |
|---|
| 605 |
Si vous trié à partir d'un champ sur un objet relié, ces champs seront |
|---|
| 606 |
ajoutés aux colonnes sélectionnées et créeront peut-être des duplicatas. |
|---|
| 607 |
Puisque les colonnes supplémentaires n'apparaissent pas dans le résultat |
|---|
| 608 |
renvoyé (elles ne sont présentes que pour le tri), cela peut parfois |
|---|
| 609 |
renvoyer des résultats non distincts. |
|---|
| 610 |
|
|---|
| 611 |
Pareillement, si vous utilisez une requête ``values()`` pour réduire |
|---|
| 612 |
les colonnes sélectionnées, ces derniÚres, utilisée dans n'importe quel |
|---|
| 613 |
``order_by()`` (ou l'ordre par défaut du modÚle) seront toujours impliqué |
|---|
| 614 |
et affecteront peut-être l'unicité des résultats. |
|---|
| 615 |
|
|---|
| 616 |
La morale, ici, est que si on utilise ``distinct()``, il faut faire attention |
|---|
| 617 |
à l'ordre par objet relié. Pareillement, lorsque l'on utilise ``distinct()`` |
|---|
| 618 |
avec ``values()``. |
|---|
| 619 |
|
|---|
| 620 |
``values(*fields)`` |
|---|
| 621 |
~~~~~~~~~~~~~~~~~~~ |
|---|
| 622 |
|
|---|
| 623 |
Renvoie un ``ValuesQuerySet`` -- Il s'agit d'un ``QuerySet`` représenté sous forme |
|---|
| 624 |
d'une liste de dictionnaires, plutÎt que d'une liste d'instances du modÚle. |
|---|
| 625 |
|
|---|
| 626 |
Chacun de ces dictionnaires représente un objet. Chaque clef correspond à une |
|---|
| 627 |
propriété du modÚle. |
|---|
| 628 |
|
|---|
| 629 |
L'exemple suivant illustre la différence de représentation entre les dictionnaires |
|---|
| 630 |
de ``values()`` et les listes d'instances habituelles:: |
|---|
| 631 |
|
|---|
| 632 |
# Cette liste contient une instance de l'objet Blog. |
|---|
| 633 |
>>> Blog.objects.filter(nom__startswith='Beatles') |
|---|
| 634 |
[Beatles Blog] |
|---|
| 635 |
|
|---|
| 636 |
# Cette liste contient un dictionnaire. |
|---|
| 637 |
>>> Blog.objects.filter(nom__startswith='Beatles').values() |
|---|
| 638 |
[{'id': 1, 'nom': 'Beatles Blog', 'titre': 'Toutes les derniÚres nouvelles des Beatles.'}] |
|---|
| 639 |
|
|---|
| 640 |
``values()`` peut recevoir une liste d'arguments, ``*fields``, qui précisent |
|---|
| 641 |
à quels champs le ``SELECT`` doit être limité. Si ces champs sont spécifiés, les dictionnaires |
|---|
| 642 |
ne contiendront que les clefs/valeurs de champ pour les champs spécifiés. A défaut, ils |
|---|
| 643 |
contiendront les clefs et les valeurs de tous les champs de la table de la base de données. |
|---|
| 644 |
|
|---|
| 645 |
Exemple:: |
|---|
| 646 |
|
|---|
| 647 |
>>> Blog.objects.values() |
|---|
| 648 |
[{'id': 1, 'nom': 'Beatles Blog', 'titre': 'Toutes les derniÚres nouvelles des Beatles.'}], |
|---|
| 649 |
>>> Blog.objects.values('id', 'nom') |
|---|
| 650 |
[{'id': 1, 'nom': 'Beatles Blog'}] |
|---|
| 651 |
|
|---|
| 652 |
Il est aussi possible de récupérer les valeurs à partir d'une relation ``ForeignKey`` |
|---|
| 653 |
en utilisant le double soulignement pour séparer le nom des champs, tout comme pour |
|---|
| 654 |
l'appel à la commande ``filter()``. Par exemple:: |
|---|
| 655 |
|
|---|
| 656 |
>>> Billet.objects.values('blog__nom').distinct() |
|---|
| 657 |
[{'nom': 'Beatles Blog'}] |
|---|
| 658 |
|
|---|
| 659 |
Il faut préciser plusieurs subtilités: |
|---|
| 660 |
|
|---|
| 661 |
* La méthode ``values()`` ne renvoie rien pour les attributs ``ManyToManyField`` |
|---|
| 662 |
et lÚvera une erreur si l'on essaye de lui passer ce type de champ. |
|---|
| 663 |
* Si l'on a un champ appelé ``foo`` de type ``ForeignKey``, l'appel par défaut |
|---|
| 664 |
à ``values()`` retournera une clé de dictionnaire appelée ``foo_id``, |
|---|
| 665 |
puisque c'est le nom de l'attribut du modÚle caché qui enregistre la valeur |
|---|
| 666 |
effective (l'attribut ``foo`` renvoie au modÚle relié). Lorsqu'on appelle |
|---|
| 667 |
``values()`` avec comme paramÚtre des noms de champ, on peut aussi passer |
|---|
| 668 |
aussi bien ``foo`` ou ``foo_id`` pour avoir le même résultat (la clé du |
|---|
| 669 |
dictionnaire correspondera le nom de champ passé en paramÚtre). |
|---|
| 670 |
|
|---|
| 671 |
Par exemple:: |
|---|
| 672 |
|
|---|
| 673 |
>>> Billet.objects.values() |
|---|
| 674 |
[{'blog_id: 1, 'titre': u'Premier billet', ...}, ...] |
|---|
| 675 |
|
|---|
| 676 |
>>> Billet.objects.values('blog') |
|---|
| 677 |
[{'blog': 1}, ...] |
|---|
| 678 |
|
|---|
| 679 |
>>> Billet.objects.values('blog_id') |
|---|
| 680 |
[{'blog_id': 1}, ...] |
|---|
| 681 |
* Lorsqu'on utilise ``values()`` conjointement à ``distinct()``, |
|---|
| 682 |
il faut être conscient que l'ordre des résultats peut être modifié. |
|---|
| 683 |
Voir la note dans la section `distinct()`_, ci-dessus, pour plus de |
|---|
| 684 |
details. |
|---|
| 685 |
|
|---|
| 686 |
**Nouveau dans la version de développement::** Auparavant, il était impossible de |
|---|
| 687 |
passer ``blog_id`` à la méthode ``values()`` dans l'exemple précédent, était accepté |
|---|
| 688 |
uniquement ``blog``. |
|---|
| 689 |
|
|---|
| 690 |
Un ``ValuesQuerySet`` est utile lorsque l'on sait qu'on ne va avoir besoin que d'un petit |
|---|
| 691 |
nombre des champs disponibles et qu'on n'aura pas besoin de la fonctionnalité globale d'un |
|---|
| 692 |
objet. C'est plus pratique de sélectionner seulement les champs dont on a besoin. |
|---|
| 693 |
|
|---|
| 694 |
Pour finir, notons qu'un ``ValuesQuerySet`` est un descendant de ``QuerySet`` et |
|---|
| 695 |
qu'il hérite donc de ses méthodes. On peut utiliser ``filter()`` ou ``order_by()``... |
|---|
| 696 |
Oui, ces 2 exemples renvoient bien le même résultat:: |
|---|
| 697 |
|
|---|
| 698 |
Blog.objects.values().order_by('id') |
|---|
| 699 |
Blog.objects.order_by('id').values() |
|---|
| 700 |
|
|---|
| 701 |
Les créateurs de Django ont préféré que les méthodes qui affectent le SQL |
|---|
| 702 |
soient devant les méthodes (optionnelles) qui affectent la représentation (comme ``values()``). |
|---|
| 703 |
Mais ça n'a pas d'importance. |
|---|
| 704 |
|
|---|
| 705 |
``values_list(*fields)`` |
|---|
| 706 |
~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 707 |
|
|---|
| 708 |
**Nouveau dans la version de développement** |
|---|
| 709 |
|
|---|
| 710 |
Ceci est une méthode similaire à ``values()`` excepté qu'au lieu de retourner |
|---|
| 711 |
une liste de dictionnaires, elle retourne une liste de tuples. Chaque tuple |
|---|
| 712 |
contient la valeur du champ respectif passé en paramÚtre lors de l'appel à |
|---|
| 713 |
``values_list()``. Le premier élément est le premier champ, etc. Par exemple:: |
|---|
| 714 |
|
|---|
| 715 |
>>> Billet.objects.values_list('id', 'titre') |
|---|
| 716 |
[(1, u'Premier billet'), ...] |
|---|
| 717 |
|
|---|
| 718 |
Si l'on passe en argument uniquement un seul champ, il est possible |
|---|
| 719 |
de passer aussi le paramÚtre ``flat``. Si celui-ci est vrai (``True``), |
|---|
| 720 |
les résultats retournés seront des valeurs simples, et non des 1-uplets. |
|---|
| 721 |
Un exemple devrait éclaircir la différence :: |
|---|
| 722 |
|
|---|
| 723 |
>>> Billet.objects.values_list('id').order_by('id') |
|---|
| 724 |
[(1,), (2,), (3,), ...] |
|---|
| 725 |
|
|---|
| 726 |
>>> Billet.objects.values_list('id', flat=True).order_by('id') |
|---|
| 727 |
[1, 2, 3, ...] |
|---|
| 728 |
|
|---|
| 729 |
C'est une erreur de passer l'argument ``flat`` lorsque plusieurs champs |
|---|
| 730 |
sont présents. |
|---|
| 731 |
|
|---|
| 732 |
Si l'on ne passe aucune valeur à ``values_list()``, cela retournera |
|---|
| 733 |
tous les champs du modÚle, dans l'ordre de déclaration. |
|---|
| 734 |
|
|---|
| 735 |
``dates(field, kind, order='ASC')`` |
|---|
| 736 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 737 |
|
|---|
| 738 |
Renvoie un ``DateQuerySet`` -- il s'agit d'un ``QuerySet`` d'objets ``datetime.datime``. |
|---|
| 739 |
Il contient autant d'éléments qu'il y a de valeurs distinctes pour le |
|---|
| 740 |
paramÚtre ``kind`` (catégorie) fourni. |
|---|
| 741 |
|
|---|
| 742 |
Le paramÚtre ``field`` doit être le nom d'un des champs de type ``DateField`` ou |
|---|
| 743 |
``DateTimeField`` de votre modÚle. |
|---|
| 744 |
|
|---|
| 745 |
Le paramÚtre ``kind`` doit être une chaîne ayant une des valeurs ``"year"``, |
|---|
| 746 |
``"month"`` ou ``"day"``. Dans le cas d'un kind ``"month"`` ou ``"day"``, les |
|---|
| 747 |
dates renvoyées sont tronquées. La précision des dates renvoyées est au niveau |
|---|
| 748 |
du paramÚtre ``kind``. |
|---|
| 749 |
|
|---|
| 750 |
* ``"year"`` renvoie une liste de toutes les années distinctes pour ce champ. |
|---|
| 751 |
* ``"month"`` renvoie une liste de tous les paires mois/année distinctes pour ce champ. |
|---|
| 752 |
* ``"day"`` renvoie une liste de toutes les dates (jour/mois/année) distinctes pour ce champ |
|---|
| 753 |
|
|---|
| 754 |
Le paramÚtre ``order`` définit dans quel ordre sont classés les résultats. L'ordre croissant |
|---|
| 755 |
``'ASC'`` est implicite, sinon on utilise ``'DESC'``. |
|---|
| 756 |
|
|---|
| 757 |
Exemples:: |
|---|
| 758 |
|
|---|
| 759 |
>>> Billet.objects.dates('date_publication', 'year') |
|---|
| 760 |
[datetime.datetime(2005, 1, 1)] |
|---|
| 761 |
>>> Billet.objects.dates('date_publication', 'month') |
|---|
| 762 |
[datetime.datetime(2005, 2, 1), datetime.datetime(2005, 3, 1)] |
|---|
| 763 |
>>> Billet.objects.dates('date_publication', 'day') |
|---|
| 764 |
[datetime.datetime(2005, 2, 20), datetime.datetime(2005, 3, 20)] |
|---|
| 765 |
>>> Billet.objects.dates('date_publication', 'day', order='DESC') |
|---|
| 766 |
[datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)] |
|---|
| 767 |
>>> Billet.objects.filter(titre__contains='Lennon').dates('date_publication', 'day') |
|---|
| 768 |
[datetime.datetime(2005, 3, 20)] |
|---|
| 769 |
|
|---|
| 770 |
``none()`` |
|---|
| 771 |
~~~~~~~~~~ |
|---|
| 772 |
|
|---|
| 773 |
**Nouveau dans la version de développement** |
|---|
| 774 |
|
|---|
| 775 |
Renvoie un ``EmptyQuerySet`` -- un ``QuerySet`` vide. Ca sert |
|---|
| 776 |
lorsque l'on sait qu'on doit renvoyer une liste vide, mais que la fonction appelante |
|---|
| 777 |
attend un résultat de type ``QuerySet``. |
|---|
| 778 |
|
|---|
| 779 |
Exemples:: |
|---|
| 780 |
|
|---|
| 781 |
>>> Billet.objects.none() |
|---|
| 782 |
[] |
|---|
| 783 |
|
|---|
| 784 |
``all()`` |
|---|
| 785 |
~~~~~~~~~~ |
|---|
| 786 |
|
|---|
| 787 |
**Nouveau dans la version de développement** |
|---|
| 788 |
|
|---|
| 789 |
Renvoie une ''copie'' du ``QuerySet`` actuel (ou une classe dérivée du ``QuerySet`` |
|---|
| 790 |
passé en paramÚtre). Cela peut être utile dans certaines situations où l'on |
|---|
| 791 |
désire passer en paramÚtre soit un manager de modÚle, soit un ``QuerySet`` et |
|---|
| 792 |
pratiquer d'autres filtrages sur le résultat. On peut appeler sûrement ``all()`` |
|---|
| 793 |
sur un objet et on aura alors un ``QuerySet`` pour d'autres opérations. |
|---|
| 794 |
|
|---|
| 795 |
``select_related()`` |
|---|
| 796 |
~~~~~~~~~~~~~~~~~~~~ |
|---|
| 797 |
|
|---|
| 798 |
Renvoie un ``QuerySet`` qui aura automatiquement "suivi" les relations |
|---|
| 799 |
de clefs étrangÚres (foreign-key). Les objets reliés par ces relations seront |
|---|
| 800 |
mis en cache pour une utilisation ultérieure. Ceci permet d'obtenir des résultats (parfois beaucoup) |
|---|
| 801 |
plus importants en volume, mais le nombre de requêtes nécessaires sera réduit lorsqu'on |
|---|
| 802 |
utilisera les relations de clefs étrangÚres ultérieurement. |
|---|
| 803 |
|
|---|
| 804 |
Cet exemple illustre la différence entre une recherche normale et une |
|---|
| 805 |
recherche qui utilise ``select_related()``. Voici une recherche normale:: |
|---|
| 806 |
|
|---|
| 807 |
# Une requête est exécutée |
|---|
| 808 |
e = Billet.objects.get(id=5) |
|---|
| 809 |
|
|---|
| 810 |
# Une autre requête est exécutée pour récupérer l'objet relié par |
|---|
| 811 |
# la relation. |
|---|
| 812 |
b = e.blog |
|---|
| 813 |
|
|---|
| 814 |
Avec ``select_related``:: |
|---|
| 815 |
|
|---|
| 816 |
# Une requête est exécutée. |
|---|
| 817 |
e = Billet.objects.select_related().get(id=5) |
|---|
| 818 |
|
|---|
| 819 |
# Aucune requête n'est exécutée, l'objet e.blog a été instancié |
|---|
| 820 |
# et mis en cache lors de la requête précédente. |
|---|
| 821 |
b = e.blog |
|---|
| 822 |
|
|---|
| 823 |
|
|---|
| 824 |
``select_related()`` suit autant de relations que possible. Soit le |
|---|
| 825 |
modÚle suivant:: |
|---|
| 826 |
|
|---|
| 827 |
class Ville(models.Model): |
|---|
| 828 |
# ... |
|---|
| 829 |
|
|---|
| 830 |
class Personne(models.Model): |
|---|
| 831 |
# ... |
|---|
| 832 |
ville_naissance = models.ForeignKey(Ville) |
|---|
| 833 |
|
|---|
| 834 |
class Livre(models.Model): |
|---|
| 835 |
# ... |
|---|
| 836 |
auteur = models.ForeignKey(Personne) |
|---|
| 837 |
|
|---|
| 838 |
L'appel de ``Livre.objects.select_related().get(id=4)`` mettra en cache |
|---|
| 839 |
l'objet ``Personne`` relié à ``Livre``, puis l'objet ``Ville`` relié à ``Personne``:: |
|---|
| 840 |
|
|---|
| 841 |
b = Livre.objects.select_related().get(id=4) |
|---|
| 842 |
p = b.auteur # Pas de requête exécutée. |
|---|
| 843 |
c = p.ville_naissance # Pas de requête exécutée. |
|---|
| 844 |
|
|---|
| 845 |
sv = Livre.objects.get(id=4) # On n'utilise pas ``select_related()`` |
|---|
| 846 |
p = b.auteur # Une requête est exécutée. |
|---|
| 847 |
c = p.ville_naissance # Une requête est exécutée. |
|---|
| 848 |
|
|---|
| 849 |
Notons que ``select_related()`` n'exploite pas les relations de clefs |
|---|
| 850 |
étrangÚres qui ont ``null=True``. |
|---|
| 851 |
|
|---|
| 852 |
En général l'emploi de ``select_related()`` améliore grandement les performances |
|---|
| 853 |
en réduisant le nombre de requêtes SQL. Par contre, dans le cas où |
|---|
| 854 |
un grand nombre de relations s'enchaînent ``select_related()`` peut parfois finir |
|---|
| 855 |
par suivre "beaucoup trop" de relations, et peut générer |
|---|
| 856 |
des requêtes si importantes que cela finit par ralentir l'application. |
|---|
| 857 |
|
|---|
| 858 |
Dans ce cas, on peut utiliser le paramÚtre ``depth`` pour contrÎler combien |
|---|
| 859 |
de niveaux de relations ``select_related()`` suivra effectivement:: |
|---|
| 860 |
|
|---|
| 861 |
b = Livre.objects.select_related(depth=1).get(id=4) |
|---|
| 862 |
p = b.auteur # Pas de requête exécuté. |
|---|
| 863 |
c = p.ville_naissance # Une requête est exécutée. |
|---|
| 864 |
|
|---|
| 865 |
L'argument ``depth`` est nouveau dans la version de développement. |
|---|
| 866 |
|
|---|
| 867 |
**Nouveau dans la version de développement:** Parfois on desire accéder qu'à |
|---|
| 868 |
un modÚle spécifique relié à notre modÚle racine, non à tous. Dans ces cas, |
|---|
| 869 |
on peut passer en argument le nom du champ relié à la méthode ``select_related()`` |
|---|
| 870 |
et cela ne suivra que cette relation. On peut aussi faire cela pour les modÚles |
|---|
| 871 |
ayant plus d'une relation en séparant les noms des champs avec un double soulignement, |
|---|
| 872 |
comme pour les filtres. Par exemple, si on a le modÚle suivant:: |
|---|
| 873 |
|
|---|
| 874 |
class Salle(models.Model): |
|---|
| 875 |
# ... |
|---|
| 876 |
immeuble = models.ForeignKey(...) |
|---|
| 877 |
|
|---|
| 878 |
class Classe(models.Model): |
|---|
| 879 |
# ... |
|---|
| 880 |
professeur = models.ForeignKey(...) |
|---|
| 881 |
salle = models.ForeignKey(Salle) |
|---|
| 882 |
sujet = models.ForeignKey(...) |
|---|
| 883 |
|
|---|
| 884 |
...et que l'on désire travailler uniquement les attribut ``salle`` et ``sujet``, |
|---|
| 885 |
on peut juste écrire:: |
|---|
| 886 |
|
|---|
| 887 |
g = Classe.objects.select_related('salle', 'sujet') |
|---|
| 888 |
|
|---|
| 889 |
Ceci est aussi valide:: |
|---|
| 890 |
|
|---|
| 891 |
g = Classe.objects.select_related('salle__immeuble', 'sujet') |
|---|
| 892 |
|
|---|
| 893 |
...et chargera la relation ``immeuble``. |
|---|
| 894 |
|
|---|
| 895 |
On peut uniquement se référer à une relation ``ForeignKey`` appartenant |
|---|
| 896 |
à la liste des champs passés en argument à ``select_related``. On *peut* |
|---|
| 897 |
faire référence à des champ ``ForeignKey`` ayant ``null=True`` (contrairement |
|---|
| 898 |
à l'appel par défaut à ``select_related()``). C'est une erreur d'utiliser à la fois |
|---|
| 899 |
une liste de champ et le paramÚtre ``depth`` dans le même appel à ``select_related()``, |
|---|
| 900 |
puisque ces options rentrent en conflit. |
|---|
| 901 |
|
|---|
| 902 |
``extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)`` |
|---|
| 903 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 904 |
|
|---|
| 905 |
Parfois la syntaxe de requête de Django ne parvient pas exprimer des clauses |
|---|
| 906 |
``WHERE`` complexes. Pour ces cas particuliers, Django fournis le modificateur |
|---|
| 907 |
de ``QuerySet`` ``extra()`` qui permet d'injecter des clauses spécifiques |
|---|
| 908 |
dans le SQL généré par un ``QuerySet``. |
|---|
| 909 |
|
|---|
| 910 |
Par définition, ces critÚres supplémentaires peuvent ne pas être portables |
|---|
| 911 |
d'un moteur de base de données à l'autre, puisqu'il s'agit en fait d'écrire |
|---|
| 912 |
du code SQL. Par ailleurs, cela viole le concept DRY (Don't Repeat Yourself). |
|---|
| 913 |
Il faut donc l'utiliser le moins possible. |
|---|
| 914 |
|
|---|
| 915 |
``extra()`` prend en paramÚtre au moins un argument parmi ``params``, ``select``, |
|---|
| 916 |
``where`` ou ``tables``. (ils ne sont pas obligatoires mais il faut |
|---|
| 917 |
en utiliser au moins un). |
|---|
| 918 |
|
|---|
| 919 |
``select`` |
|---|
| 920 |
L'argument ``select`` permet d'ajouter des champs supplémentaires à la clause |
|---|
| 921 |
``SELECT``. Cela sera sous la forme d'un dictionnaire, dont la clef est le |
|---|
| 922 |
nom du champ calculé, et la valeur, la formule permettant de le calculer. |
|---|
| 923 |
|
|---|
| 924 |
Exemple:: |
|---|
| 925 |
|
|---|
| 926 |
Billet.objects.extra(select={'is_recent': "date_publication > '2006-01-01'"}) |
|---|
| 927 |
|
|---|
| 928 |
Le résultat sera donc une liste d'objets ``Billet`` ayant une propriété |
|---|
| 929 |
supplémentaire : un booléen nommé ``is_recent`` à True si le champ |
|---|
| 930 |
``date_publication`` est une date supérieure au 1er janvier 2006. |
|---|
| 931 |
|
|---|
| 932 |
Django injecte le petit bout de SQL directement dans l'instruction ``SELECT``. |
|---|
| 933 |
La requête ressemblera à ça:: |
|---|
| 934 |
|
|---|
| 935 |
SELECT blog_billet.*, (date_publication > '2006-01-01') |
|---|
| 936 |
FROM blog_billet; |
|---|
| 937 |
|
|---|
| 938 |
L'exemple suivant est plus compliqué; il exécute une sous requête en donnant |
|---|
| 939 |
à chaque objet ``Blog`` du résultat de la requête principale, une propriété |
|---|
| 940 |
``compteur_billet``, qui est le nombre d'objets ``Billet`` associés:: |
|---|
| 941 |
|
|---|
| 942 |
Blog.objects.extra( |
|---|
| 943 |
select={ |
|---|
| 944 |
'compteur_billet': 'SELECT COUNT(*) FROM blog_Billet WHERE blog_billet.blog_id = blog_blog.id' |
|---|
| 945 |
}, |
|---|
| 946 |
) |
|---|
| 947 |
|
|---|
| 948 |
(Dans ce cas précis, on exploite le fait que la requête fera déjà référence |
|---|
| 949 |
à la table ``blog_blog`` dans sa clause ``FROM``) |
|---|
| 950 |
|
|---|
| 951 |
La requête SQL générée ressemblera à ça:: |
|---|
| 952 |
|
|---|
| 953 |
SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_Billet WHERE blog_Billet.blog_id = blog_blog.id) |
|---|
| 954 |
FROM blog_blog; |
|---|
| 955 |
|
|---|
| 956 |
Notons que les parenthÚses nécessaires à la syntaxe des sous requêtes |
|---|
| 957 |
pour la plupart des moteurs de base de données ne le sont pas pour les clauses ``select`` |
|---|
| 958 |
de Django. Par ailleurs certains moteurs de base de données ne supportent pas les |
|---|
| 959 |
sous requêtes (certaines anciennes versions de MySQL par exemple). |
|---|
| 960 |
|
|---|
| 961 |
**Nouveau dans la version de développement** |
|---|
| 962 |
Dans quelques rares cas, on désire passer des paramÚtres aux fragments SQL |
|---|
| 963 |
dans ``extra(select=...)``. Pour cela, on utilise le paramÚtre ``select_params``. |
|---|
| 964 |
Puisque ``select_params`` est une séquence et l'attribut ``select`` un dictionnaire, |
|---|
| 965 |
il faut faire attention à ce que les paramÚtres correspondent avec les parties |
|---|
| 966 |
supplémentaire sélectionnées. Dans cette situation, il faut utiliser un dictionnaire |
|---|
| 967 |
trié (``django.utils.datastructures.SortedDict``) pour la valeur sélectionnée, et non |
|---|
| 968 |
pas un dictionnaire normal de Python. |
|---|
| 969 |
|
|---|
| 970 |
Cela fonctionnera, par exemple:: |
|---|
| 971 |
|
|---|
| 972 |
Blog.objects.extra( |
|---|
| 973 |
select=SortedDict(('a', '%s'), ('b', '%s')), |
|---|
| 974 |
select_params=('un', 'deux')) |
|---|
| 975 |
|
|---|
| 976 |
``where`` / ``tables`` |
|---|
| 977 |
On peut ajouter du code SQL à la clause ``WHERE`` en utilisant le paramÚtre |
|---|
| 978 |
``where``, par exemple pour créer une jointure particuliÚre. On peut ajouter manuellement |
|---|
| 979 |
des tables à la clause ``FROM`` en utilisant le paramÚtre ``tables``. |
|---|
| 980 |
|
|---|
| 981 |
``where`` et ``tables`` acceptent l'un et l'autre une liste de chaînes. |
|---|
| 982 |
Tous les paramÚtres de ``where`` sont ajoutés aux éventuelles autres conditions |
|---|
| 983 |
de la clause ``WHERE`` avec un ``AND``. |
|---|
| 984 |
|
|---|
| 985 |
Exemple:: |
|---|
| 986 |
|
|---|
| 987 |
Billet.objects.extra(where=['id IN (3, 4, 5, 20)']) |
|---|
| 988 |
|
|---|
| 989 |
...donnera, en gros, en SQL:: |
|---|
| 990 |
|
|---|
| 991 |
SELECT * FROM blog_Billet WHERE id IN (3, 4, 5, 20); |
|---|
| 992 |
|
|---|
| 993 |
Attention lorsqu'on utilise le paramÚtre ``tables``, si l'on spécifie |
|---|
| 994 |
des tables déjà utilisées dans la requête. Lorsqu'on ajoute des tables |
|---|
| 995 |
supplémentaires via le paramÚtre ``tables``, Django suppose que l'on |
|---|
| 996 |
que désire que cette table soit incluse une fois supplémentaire, si elle |
|---|
| 997 |
est déjà incluse. Ceci pose problÚme, puisque le nom de la table aura |
|---|
| 998 |
un alias. Si une table apparaît plusieurs fois dans une requête SQL, les |
|---|
| 999 |
occurrences suivantes doivent utiliser un alias pour que la base de données |
|---|
| 1000 |
puisse les différencier. Si l'on fait référence à la table supplémentaire, |
|---|
| 1001 |
que l'on a ajouté dans le paramÚtre ``where``, cela provoquera des erreurs. |
|---|
| 1002 |
|
|---|
| 1003 |
Normalement, on ajoute seulement des tables supplémentaires qui n'apparaissent |
|---|
| 1004 |
pas dans la requête. Cependant, si le cas décrit précédemment apparaît, il |
|---|
| 1005 |
existe quelques solutions. PremiÚrement, essayez de ne pas utiliser la table |
|---|
| 1006 |
supplémentaire mais celle appartenant déjà à la requête. Si cela n'est pas |
|---|
| 1007 |
possible, ajoutez l'appel ``extra()`` au début de la construction du ``QuerySet`` |
|---|
| 1008 |
pour que votre table soit la premiÚre utilisation de cette table. Finalement, |
|---|
| 1009 |
en dernier recours, regardez la requête produite et réécrivez votre |
|---|
| 1010 |
``where`` avec un alias pour la table supplémentaire. Cet alias sera le même |
|---|
| 1011 |
à chaque fois que vous construirez un ``QuerySet`` de la même façon. |
|---|
| 1012 |
|
|---|
| 1013 |
``order_by`` |
|---|
| 1014 |
Si l'on désire classer le ``QuerySet`` renvoyé en utilisant des champs |
|---|
| 1015 |
ou tables inclus via ``extra()``, on peut utiliser le paramÚtre ``order_by`` |
|---|
| 1016 |
à ``extra()`` et lui passer une séquence de chaîne de caractÚres. Ces chaînes |
|---|
| 1017 |
de caractÚres doivent être soit des champs de modÚle (comme dans la méthode |
|---|
| 1018 |
``order_by()`` normal des ``QuerySet``), soit de la forme ``nom_table.nom_colonne`` |
|---|
| 1019 |
ou un alias pour la colonne spécifiée grâce au paramÚtre ``select`` dans ``extra()``. |
|---|
| 1020 |
|
|---|
| 1021 |
Par exemple:: |
|---|
| 1022 |
|
|---|
| 1023 |
q = Billet.objects.extra(select={'est_recent': "date_publication > '2006-01-01'"}) |
|---|
| 1024 |
q = q.extra(order_by = ['-est_recent']) |
|---|
| 1025 |
|
|---|
| 1026 |
Cela classera tous les éléments, pour lesquels ``est_recent`` est vrai, |
|---|
| 1027 |
au début de l'ensemble résultant (``True`` est classé avant ``False`` dans l'ordre |
|---|
| 1028 |
décroissant). |
|---|
| 1029 |
|
|---|
| 1030 |
Cela montre, par ailleurs, que vous pouvez faire plusieurs appels à ``extra()`` |
|---|
| 1031 |
et que cela se comportera comme vous le souhaitez (ajouter des nouvelles contraintes |
|---|
| 1032 |
à chaque fois). |
|---|
| 1033 |
|
|---|
| 1034 |
``params`` |
|---|
| 1035 |
|
|---|
| 1036 |
Les paramÚtres ``select`` et ``where`` décrits ci-dessus peuvent utiliser |
|---|
| 1037 |
la syntaxe Python de remplacement de chaîne -- `'%s'`` -- pour indiquer |
|---|
| 1038 |
quels paramÚtres le moteur de base de données doit mettre entre guillemets. |
|---|
| 1039 |
L'argument ``params`` est la liste des éventuels paramÚtres supplémentaires à substituer. |
|---|
| 1040 |
|
|---|
| 1041 |
Exemple:: |
|---|
| 1042 |
|
|---|
| 1043 |
Billet.objects.extra(where=['titre=%s'], params=['Lennon']) |
|---|
| 1044 |
|
|---|
| 1045 |
Utilisez toujours ``params`` plutÃŽt que d'imbriquer directement les valeurs |
|---|
| 1046 |
dans ``select`` ou ``where``. Avec ``params`` on est sûr que les guillemets |
|---|
| 1047 |
utilisés correspondent au moteur de base de données, ou que d'éventuels |
|---|
| 1048 |
guillemets seront correctement échappés. |
|---|
| 1049 |
|
|---|
| 1050 |
Ne pas faire:: |
|---|
| 1051 |
|
|---|
| 1052 |
Billet.objects.extra(where=["titre='Lennon'"]) |
|---|
| 1053 |
|
|---|
| 1054 |
Mais faire:: |
|---|
| 1055 |
|
|---|
| 1056 |
Billet.objects.extra(where=['titre=%s'], params=['Lennon']) |
|---|
| 1057 |
|
|---|
| 1058 |
**Nouveau dans la version de développement:** L'argument ``select_params`` d'``extra()`` |
|---|
| 1059 |
est nouveau. Avant, on pouvait essayer de passer des paramÚtres pour ``select`` |
|---|
| 1060 |
dans l'argument ``params``, mais cela était trÚs instable. |
|---|
| 1061 |
|
|---|
| 1062 |
Les méthodes de ``QuerySets`` qui ne renvoient pas de ``QuerySet`` |
|---|
| 1063 |
------------------------------------------------------------------ |
|---|
| 1064 |
|
|---|
| 1065 |
Les méthodes suivantes évaluent le ``QuerySet`` duquel elles sont appelées |
|---|
| 1066 |
et renvoient *autre chose* qu'un ``QuerySet``. |
|---|
| 1067 |
|
|---|
| 1068 |
Ces méthodes n'utilisent pas le cache (voir plus bas `Les QuerySets et leurs caches`_). |
|---|
| 1069 |
Elles exécutent une requête SQL à chaque fois qu'elles sont appelées. |
|---|
| 1070 |
|
|---|
| 1071 |
``get(**kwargs)`` |
|---|
| 1072 |
~~~~~~~~~~~~~~~~~ |
|---|
| 1073 |
|
|---|
| 1074 |
Renvoie l'objet correspondant au paramÚtre de recherche donné. Ce paramÚtre |
|---|
| 1075 |
doit être fourni au format décrit dans `Recherche sur un champ`_. |
|---|
| 1076 |
|
|---|
| 1077 |
``get()`` lÚve une exception ``MultipleObjetcsReturned`` si plusieurs objets sont trouvés. |
|---|
| 1078 |
L'exception ``MultipleObjetcsReturned`` est un attribut de la classe modÚle. Par exemple, |
|---|
| 1079 |
ce qui suit lÚvera une exception ``MultipleObjetcsReturned`` s'il y a plus d'un auteur |
|---|
| 1080 |
ayant pour nom 'John':: |
|---|
| 1081 |
|
|---|
| 1082 |
Auteur.objects.get(nom='John') # lÚve Auteur.MultipleObjectsReturned |
|---|
| 1083 |
|
|---|
| 1084 |
``get()`` lÚve une exception ``DoesNotExist`` si aucun objet n'est trouvé. L'exception |
|---|
| 1085 |
``DoesNotExist`` est une propriété de la classe sur modÚle. Exemple:: |
|---|
| 1086 |
|
|---|
| 1087 |
Billet.objects.get(id='foo') # lÚve l'exception Billet.DoesNotExist |
|---|
| 1088 |
|
|---|
| 1089 |
L'exception ``DoesNotExist`` hérite de ``django.core.exceptions.ObjectDoesNotExist``, |
|---|
| 1090 |
on peut donc intercepter plusieurs exceptions ``DoesNotExist`` dans le même |
|---|
| 1091 |
bloc ``except``. Exemple:: |
|---|
| 1092 |
|
|---|
| 1093 |
|
|---|
| 1094 |
from django.core.exceptions import ObjectDoesNotExist |
|---|
| 1095 |
try: |
|---|
| 1096 |
e = Billet.objects.get(id=3) |
|---|
| 1097 |
b = Blog.objects.get(id=1) |
|---|
| 1098 |
except ObjectDoesNotExist: |
|---|
| 1099 |
print "L'objet Billet ou l'objet Blog n'existe pas." |
|---|
| 1100 |
|
|---|
| 1101 |
``create(**kwargs)`` |
|---|
| 1102 |
~~~~~~~~~~~~~~~~~~~~ |
|---|
| 1103 |
|
|---|
| 1104 |
Méthode pratique pour la création d'un objet et son enregistrement en une seule ligne. Ainsi:: |
|---|
| 1105 |
|
|---|
| 1106 |
p = Personne.objects.create(prenom="Bruce", nom="Springsteen") |
|---|
| 1107 |
|
|---|
| 1108 |
et:: |
|---|
| 1109 |
|
|---|
| 1110 |
p = Personne(prenom="Bruce", nom="Springsteen") |
|---|
| 1111 |
p.save() |
|---|
| 1112 |
|
|---|
| 1113 |
sont équivalents. |
|---|
| 1114 |
|
|---|
| 1115 |
``get_or_create(**kwargs)`` |
|---|
| 1116 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 1117 |
|
|---|
| 1118 |
Méthode pratique pour rechercher un objet selon certains critÚres de |
|---|
| 1119 |
recherche, ou le créer si il n'existe pas. |
|---|
| 1120 |
|
|---|
| 1121 |
Cela renvoie un tuple ``(object, created)``, ou ``object`` est l'objet récupéré |
|---|
| 1122 |
ou créé, et ``created`` un booléen indiquant si l'objet a été créé ou pas. |
|---|
| 1123 |
|
|---|
| 1124 |
Ca permet d'éviter un paragraphe standard de code, et c'est surtout pratique pour les |
|---|
| 1125 |
scripts d'importations de données. Par exemple:: |
|---|
| 1126 |
|
|---|
| 1127 |
try: |
|---|
| 1128 |
obj = Personne.objects.get(prenom='John', nom='Lennon') |
|---|
| 1129 |
except Personne.DoesNotExist: |
|---|
| 1130 |
obj = Personne(prenom='John', nom='Lennon', date_naissance=date(1940, 10, 9)) |
|---|
| 1131 |
obj.save() |
|---|
| 1132 |
|
|---|
| 1133 |
Grâce à ``get_or_create()``, on peut abréger le code ci-dessus, surtout dans le |
|---|
| 1134 |
cas d'un modÚle complexe où le nombre de champs augmente:: |
|---|
| 1135 |
|
|---|
| 1136 |
obj, created = Personne.objects.get_or_create(prenom='John', nom='Lennon', |
|---|
| 1137 |
defaults={'date_naissance': date(1940, 10, 9)}) |
|---|
| 1138 |
|
|---|
| 1139 |
Tous les arguments passés à ``get_or_create()`` -- à part ``defaults`` qui est |
|---|
| 1140 |
optionnel -- seront utilisés lors du ``get()``. Si un objet est trouvé, |
|---|
| 1141 |
``get_or_create()`` renvoie un tuple constitué de cet objet et du booléen ``False``. |
|---|
| 1142 |
Si *aucun* objet n'est trouvé, ``get_or_create()`` instanciera puis enregistrera un |
|---|
| 1143 |
nouvel objet, puis renverra un tuple constitué de ce nouvel objet et du booléen |
|---|
| 1144 |
``True``. Ce nouvel objet sera créé selon l'algorithme suivant:: |
|---|
| 1145 |
|
|---|
| 1146 |
defaults = kwargs.pop('defaults', {}) |
|---|
| 1147 |
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k]) |
|---|
| 1148 |
params.update(defaults) |
|---|
| 1149 |
obj = self.model(**params) |
|---|
| 1150 |
obj.save() |
|---|
| 1151 |
|
|---|
| 1152 |
En français, ça signifie : qui commence par n'importe lequel des arguments |
|---|
| 1153 |
de recherche qui ne soient pas dans ``'defaults'`` et qui ne contient pas un double |
|---|
| 1154 |
soulignement (ce qui indiquerait une recherche non-exacte). Puis ajoutez le contenu |
|---|
| 1155 |
de ``defaults``, en écrasant une clef si nécessaire, et utilisez le résultat en |
|---|
| 1156 |
tant qu'argument mot-clef pour la classe du modÚle. |
|---|
| 1157 |
|
|---|
| 1158 |
Si vous aviez un champ ``defaults`` et vouliez faire une recherche exacte dessus, |
|---|
| 1159 |
il faut utiliser ``'defaults__exact'``, comme suit:: |
|---|
| 1160 |
|
|---|
| 1161 |
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'}) |
|---|
| 1162 |
|
|---|
| 1163 |
Un dernier mot à propos de ``get_or_create()`` dans les vues. Comme mentionné |
|---|
| 1164 |
plus haut, ``get_or_create()`` est surtout utile dans des scripts pour analyser |
|---|
| 1165 |
les données. Si vous êtes amené à utiliser ``get_or_create()`` dans une vue, |
|---|
| 1166 |
il faut vraiment le faire que dans une requête ``POST`` à moins d'avoir une bonne raison. |
|---|
| 1167 |
Pour plus d'information, voyez `Safe methods`_ dans la RFC de HTTP. |
|---|
| 1168 |
|
|---|
| 1169 |
.. _Safe methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 |
|---|
| 1170 |
|
|---|
| 1171 |
``count()`` |
|---|
| 1172 |
~~~~~~~~~~~ |
|---|
| 1173 |
|
|---|
| 1174 |
Renvoie un entier représentant le nombre d'objets de la base correspondant au |
|---|
| 1175 |
``QuerySet``. ``count()`` ne lÚve jamais d'exception. |
|---|
| 1176 |
|
|---|
| 1177 |
Exemple:: |
|---|
| 1178 |
|
|---|
| 1179 |
# Retourne le nombre total d'objets billet dans la base |
|---|
| 1180 |
Billet.objects.count() |
|---|
| 1181 |
|
|---|
| 1182 |
# Retourne le nombre total d'objets billet dont la propriété titre |
|---|
| 1183 |
# contient 'Lennon' |
|---|
| 1184 |
Billet.objects.filter(titre__contains='Lennon').count() |
|---|
| 1185 |
|
|---|
| 1186 |
Au niveau SQL, ``count()`` exécute un ``SELECT COUNT(*)``. Il faut toujours |
|---|
| 1187 |
utiliser ``count()`` plutÃŽt que d'appeler la fonction ``len()`` sur un objet |
|---|
| 1188 |
Python contenant tous les objets d'une table. |
|---|
| 1189 |
|
|---|
| 1190 |
Selon le moteur de base de données utilisé, (PostgreSQL ou MySQL), |
|---|
| 1191 |
``count()`` peut renvoyer un entier long plutÃŽt qu'un entier Python. C'est une |
|---|
| 1192 |
petite bizarrerie de l'implémentation sous-jacente qui ne devrait pas poser de |
|---|
| 1193 |
vrai problÚme. |
|---|
| 1194 |
|
|---|
| 1195 |
|
|---|
| 1196 |
``in_bulk(id_list)`` |
|---|
| 1197 |
~~~~~~~~~~~~~~~~~~~~ |
|---|
| 1198 |
|
|---|
| 1199 |
Accepte une liste de champs de clef primaire et renvoie un dictionnaire établissant |
|---|
| 1200 |
la correspondance entre chaque champ de clef primaire et une instance de l'objet |
|---|
| 1201 |
pour l'ID donné. |
|---|
| 1202 |
|
|---|
| 1203 |
Exemple:: |
|---|
| 1204 |
|
|---|
| 1205 |
>>> Blog.objects.in_bulk([1]) |
|---|
| 1206 |
{1: Le Blog des Beatles} |
|---|
| 1207 |
>>> Blog.objects.in_bulk([1, 2]) |
|---|
| 1208 |
{1: Le Blog des Beatles, 2: Parlons du Camembert} |
|---|
| 1209 |
>>> Blog.objects.in_bulk([]) |
|---|
| 1210 |
{} |
|---|
| 1211 |
|
|---|
| 1212 |
Si l'on fournit une liste vide à ``in_bulk()``, on obtient un dictionnaire vide. |
|---|
| 1213 |
|
|---|
| 1214 |
``iterator()`` |
|---|
| 1215 |
~~~~~~~~~~~~~~ |
|---|
| 1216 |
|
|---|
| 1217 |
Evalue le ``QuerySet`` (en effectuant un requête) et renvoie un `itérateur`_ |
|---|
| 1218 |
vers les résultats. Un ``QuerySet`` lira tous ces résultats |
|---|
| 1219 |
et instanciera tous les objets correspondants dÚs le premier accÚs. A l'inverse, |
|---|
| 1220 |
``iterator()`` lira les résultats et instanciera les objets par paquet, |
|---|
| 1221 |
au fur et à mesure. Pour un ``QuerySet`` qui retourne un nombre important d'objets, |
|---|
| 1222 |
ceci permet d'obtenir de meilleures performances et de réduire considérablement |
|---|
| 1223 |
la mémoire utilisée. |
|---|
| 1224 |
|
|---|
| 1225 |
Remarquez que l'utilisation d'``iterator()`` sur un ``QuerySet`` ayant déjà |
|---|
| 1226 |
été évalué forcera une nouvelle évaluation de celui-ci, dupliquant ainsi la requête. |
|---|
| 1227 |
|
|---|
| 1228 |
.. _itérateur: http://www.python.org/dev/peps/pep-0234/ |
|---|
| 1229 |
|
|---|
| 1230 |
``latest(field_name=None)`` |
|---|
| 1231 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 1232 |
|
|---|
| 1233 |
Renvoie le dernier objet entré dans la table, par rapport à la date, en utilisant |
|---|
| 1234 |
comme champ de date le champ ``field_name`` fourni. |
|---|
| 1235 |
|
|---|
| 1236 |
Dans cet exemple, le dernier ``Billet`` de la table est renvoyé, selon le champ |
|---|
| 1237 |
``date_publication`` :: |
|---|
| 1238 |
|
|---|
| 1239 |
Billet.objects.latest('date_publication') |
|---|
| 1240 |
|
|---|
| 1241 |
Si la classe ``Meta`` du modÚle fournit l'attribut ``get_latest_by``, |
|---|
| 1242 |
on peut laisser vide l'argument ``field_name`` de ``latest()``. Django |
|---|
| 1243 |
utilisera le champ spécifié dans ``get_latest_by`` par défaut. |
|---|
| 1244 |
|
|---|
| 1245 |
Tout comme ``get()``, ``latest()`` lÚvera l'exception ``DoesNotExist`` si aucun |
|---|
| 1246 |
objet n'existe pour les paramÚtres donnés. |
|---|
| 1247 |
|
|---|
| 1248 |
Notez que ``latest()`` existe simplement pour des raisons pratiques et de lisibilité. |
|---|
| 1249 |
|
|---|
| 1250 |
Recherche sur un champ |
|---|
| 1251 |
---------------------- |
|---|
| 1252 |
|
|---|
| 1253 |
La recherche sur un champ vous permet de spécifier le contenu de la clause |
|---|
| 1254 |
``WHERE`` d'une requête SQL. Elle est spécifiée en tant qu'argument mot-clef |
|---|
| 1255 |
des méthodes de ``QuerySet`` ``filter()``, ``exclude()`` et ``get()``. |
|---|
| 1256 |
|
|---|
| 1257 |
Une recherche de base sur l'argument mot-clef prend la forme ``champ__typederecherche=valeur``. |
|---|
| 1258 |
(Il s'agit d'un double soulignement). Par exemple:: |
|---|
| 1259 |
|
|---|
| 1260 |
Billet.objects.filter(date_publication__lte='2006-01-01') |
|---|
| 1261 |
|
|---|
| 1262 |
peut être traduit (grossiÚrement) par la requête SQL suivante:: |
|---|
| 1263 |
|
|---|
| 1264 |
SELECT * FROM blog_billet WHERE date_publication <= '2006-01-01'; |
|---|
| 1265 |
|
|---|
| 1266 |
.. admonition:: Comment cela est possible ? |
|---|
| 1267 |
|
|---|
| 1268 |
Python a la capacité de définir des fonctions acceptant des arguments nom-valeur |
|---|
| 1269 |
arbitraires dont les noms et valeurs seront évalués à l'exécution. Pour plus |
|---|
| 1270 |
d'informations, voir `Arguments mot-clef`_ dans le tutoriel Python officiel. |
|---|
| 1271 |
|
|---|
| 1272 |
.. _`Arguments mot-clef`: http://docs.python.org/tut/node6.html#SECTION006720000000000000000 |
|---|
| 1273 |
|
|---|
| 1274 |
Si on passe en argument un mot-clef invalide, la fonction de recherche lÚvera |
|---|
| 1275 |
l'exception ``TypeError``. |
|---|
| 1276 |
|
|---|
| 1277 |
L'API de la base de données accepte les types de recherche suivants: |
|---|
| 1278 |
|
|---|
| 1279 |
exact |
|---|
| 1280 |
~~~~~ |
|---|
| 1281 |
|
|---|
| 1282 |
Correspondance exacte. Si la valeur fournie pour la comparaison est ``None``, |
|---|
| 1283 |
elle sera interprétée en tant que SQL ``NULL`` (Voir isnull_ pour plus de détails). |
|---|
| 1284 |
|
|---|
| 1285 |
Exemples:: |
|---|
| 1286 |
|
|---|
| 1287 |
Billet.objects.get(id__exact=14) |
|---|
| 1288 |
Billet.objects.get(id__exact=None) |
|---|
| 1289 |
|
|---|
| 1290 |
Equivalents SQL :: |
|---|
| 1291 |
|
|---|
| 1292 |
SELECT ... WHERE id = 14; |
|---|
| 1293 |
SELECT ... WHERE id = IS NULL; |
|---|
| 1294 |
|
|---|
| 1295 |
**Nouveau dans la version de développement:** La sémantique de ``id__exact=None`` a changé |
|---|
| 1296 |
dans la version de développement. Auparavant, elle était (intentionnellement) convertie |
|---|
| 1297 |
en ``WHERE id = NULL`` au niveau SQL, ce qui ne correspondait avec aucune données enregistrée. |
|---|
| 1298 |
Suite à une modification, elle se comporte maintenant comme ``id__isnull=True``. |
|---|
| 1299 |
|
|---|
| 1300 |
iexact |
|---|
| 1301 |
~~~~~~ |
|---|
| 1302 |
|
|---|
| 1303 |
Correspondance exacte insensible à la casse. |
|---|
| 1304 |
|
|---|
| 1305 |
Exemple:: |
|---|
| 1306 |
|
|---|
| 1307 |
Blog.objects.get(nom__iexact='beatles blog') |
|---|
| 1308 |
|
|---|
| 1309 |
Equivalent SQL :: |
|---|
| 1310 |
|
|---|
| 1311 |
SELECT ... WHERE nom ILIKE 'beatles blog'; |
|---|
| 1312 |
|
|---|
| 1313 |
Notez que cela correspondera à ``'Beatles Blog'``, ``'beatles blog'``, |
|---|
| 1314 |
``'BeAtLes BLoG'``, etc. |
|---|
| 1315 |
|
|---|
| 1316 |
contains |
|---|
| 1317 |
~~~~~~~~ |
|---|
| 1318 |
|
|---|
| 1319 |
Test de contenu sensible à la casse. |
|---|
| 1320 |
|
|---|
| 1321 |
Exemple:: |
|---|
| 1322 |
|
|---|
| 1323 |
Billet.objects.get(titre__contains='Lennon') |
|---|
| 1324 |
|
|---|
| 1325 |
Equivalent SQL :: |
|---|
| 1326 |
|
|---|
| 1327 |
SELECT ... WHERE titre LIKE '%Lennon%'; |
|---|
| 1328 |
|
|---|
| 1329 |
Notez que cela correspondera au titre ``'On rend hommage à Lennon '`` mais pas à |
|---|
| 1330 |
``'on rend hommage à lennon'``. |
|---|
| 1331 |
|
|---|
| 1332 |
SQLite ne supporte pas les déclarations ``LIKE`` sensibles à la casse. ``contains`` |
|---|
| 1333 |
se comportera comme ``icontains`` pour SQLite. |
|---|
| 1334 |
|
|---|
| 1335 |
icontains |
|---|
| 1336 |
~~~~~~~~~ |
|---|
| 1337 |
|
|---|
| 1338 |
Test de contenu insensible à la casse. |
|---|
| 1339 |
|
|---|
| 1340 |
Exemple:: |
|---|
| 1341 |
|
|---|
| 1342 |
Billet.objects.get(titre__icontains='Lennon') |
|---|
| 1343 |
|
|---|
| 1344 |
Equivalent SQL:: |
|---|
| 1345 |
|
|---|
| 1346 |
SELECT ... WHERE titre ILIKE '%Lennon%'; |
|---|
| 1347 |
|
|---|
| 1348 |
gt |
|---|
| 1349 |
~~ |
|---|
| 1350 |
|
|---|
| 1351 |
Supérieur à . |
|---|
| 1352 |
|
|---|
| 1353 |
Exemple:: |
|---|
| 1354 |
|
|---|
| 1355 |
Billet.objects.filter(id__gt=4) |
|---|
| 1356 |
|
|---|
| 1357 |
Equivalent SQL:: |
|---|
| 1358 |
|
|---|
| 1359 |
SELECT ... WHERE id > 4; |
|---|
| 1360 |
|
|---|
| 1361 |
gte |
|---|
| 1362 |
~~~ |
|---|
| 1363 |
|
|---|
| 1364 |
Supérieur ou égal à . |
|---|
| 1365 |
|
|---|
| 1366 |
lt |
|---|
| 1367 |
~~ |
|---|
| 1368 |
|
|---|
| 1369 |
Inférieur à . |
|---|
| 1370 |
|
|---|
| 1371 |
lte |
|---|
| 1372 |
~~~ |
|---|
| 1373 |
|
|---|
| 1374 |
Inférieur ou égal à . |
|---|
| 1375 |
|
|---|
| 1376 |
in |
|---|
| 1377 |
~~ |
|---|
| 1378 |
|
|---|
| 1379 |
Appartient à la liste donnée. |
|---|
| 1380 |
|
|---|
| 1381 |
Exemple:: |
|---|
| 1382 |
|
|---|
| 1383 |
Billet.objects.filter(id__in=[1, 3, 4]) |
|---|
| 1384 |
|
|---|
| 1385 |
Equivalent SQL:: |
|---|
| 1386 |
|
|---|
| 1387 |
SELECT ... WHERE id IN (1, 3, 4); |
|---|
| 1388 |
|
|---|
| 1389 |
Il est aussi possible d'utiliser un ``QuerySet`` pour évaluer dynamiquement |
|---|
| 1390 |
la liste de valeurs au lieu de fournir une liste de valeurs littérales. Le |
|---|
| 1391 |
``QuerySet`` doit être réduit en une liste de valeurs individuelles en utilisant |
|---|
| 1392 |
la méthode ``values()``, et puis convertit en une requête en utilisant l'attribut |
|---|
| 1393 |
``query``:: |
|---|
| 1394 |
|
|---|
| 1395 |
Billet.objects.filter(blog__in=Blog.objects.filter(nom__contains='Fromage').values('pk').query) |
|---|
| 1396 |
|
|---|
| 1397 |
Ce ``QuerySet`` sera évalué en tant que sous-sélection:: |
|---|
| 1398 |
|
|---|
| 1399 |
SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Fromage%') |
|---|
| 1400 |
|
|---|
| 1401 |
startswith |
|---|
| 1402 |
~~~~~~~~~~ |
|---|
| 1403 |
|
|---|
| 1404 |
Commence par (sensible à la casse). |
|---|
| 1405 |
|
|---|
| 1406 |
Exemple:: |
|---|
| 1407 |
|
|---|
| 1408 |
Billet.objects.filter(titre__startswith='Will') |
|---|
| 1409 |
|
|---|
| 1410 |
Equivalent SQL:: |
|---|
| 1411 |
|
|---|
| 1412 |
SELECT ... WHERE titre LIKE 'Will%'; |
|---|
| 1413 |
|
|---|
| 1414 |
SQLite ne supporte pas les déclarations ``LIKE`` sensibles à la casse. ``startswith`` |
|---|
| 1415 |
se comportera comme ``istartswith`` pour SQLite. |
|---|
| 1416 |
|
|---|
| 1417 |
istartswith |
|---|
| 1418 |
~~~~~~~~~~~ |
|---|
| 1419 |
|
|---|
| 1420 |
Commence par (insensible à la casse). |
|---|
| 1421 |
|
|---|
| 1422 |
Exemple:: |
|---|
| 1423 |
|
|---|
| 1424 |
Billet.objects.filter(titre__istartswith='will') |
|---|
| 1425 |
|
|---|
| 1426 |
Equivalent SQL:: |
|---|
| 1427 |
|
|---|
| 1428 |
SELECT ... WHERE titre ILIKE 'Will%'; |
|---|
| 1429 |
|
|---|
| 1430 |
endswith |
|---|
| 1431 |
~~~~~~~~ |
|---|
| 1432 |
|
|---|
| 1433 |
Termine par (sensible à la casse). |
|---|
| 1434 |
|
|---|
| 1435 |
Exemple:: |
|---|
| 1436 |
|
|---|
| 1437 |
Billet.objects.filter(titre__endswith='cats') |
|---|
| 1438 |
|
|---|
| 1439 |
Equivalent SQL:: |
|---|
| 1440 |
|
|---|
| 1441 |
SELECT ... WHERE titre LIKE '%cats'; |
|---|
| 1442 |
|
|---|
| 1443 |
SQLite ne supporte pas les déclarations ``LIKE`` sensibles à la casse. ``endswith`` |
|---|
| 1444 |
se comportera comme ``iendswith`` pour SQLite. |
|---|
| 1445 |
|
|---|
| 1446 |
iendswith |
|---|
| 1447 |
~~~~~~~~~ |
|---|
| 1448 |
|
|---|
| 1449 |
Termine par (insensible à la casse). |
|---|
| 1450 |
|
|---|
| 1451 |
Exemple:: |
|---|
| 1452 |
|
|---|
| 1453 |
Billet.objects.filter(titre__iendswith='will') |
|---|
| 1454 |
|
|---|
| 1455 |
Equivalent SQL:: |
|---|
| 1456 |
|
|---|
| 1457 |
SELECT ... WHERE titre ILIKE '%will' |
|---|
| 1458 |
|
|---|
| 1459 |
range |
|---|
| 1460 |
~~~~~ |
|---|
| 1461 |
|
|---|
| 1462 |
Test de plage (inclusif). |
|---|
| 1463 |
|
|---|
| 1464 |
Exemple:: |
|---|
| 1465 |
|
|---|
| 1466 |
date_debut = datetime.date(2005, 1, 1) |
|---|
| 1467 |
date_fin = datetime.date(2005, 3, 31) |
|---|
| 1468 |
Billet.objects.filter(date_publication__range=(date_debut, date_fin)) |
|---|
| 1469 |
|
|---|
| 1470 |
Equivalent SQL:: |
|---|
| 1471 |
|
|---|
| 1472 |
SELECT ... WHERE date_publication BETWEEN '2005-01-01' and '2005-03-31'; |
|---|
| 1473 |
|
|---|
| 1474 |
On peut utiliser ``range`` partout où on peut utiliser ``BETWEEN`` |
|---|
| 1475 |
en SQL (pour les dates, les nombres et même les caractÚres). |
|---|
| 1476 |
|
|---|
| 1477 |
year |
|---|
| 1478 |
~~~~ |
|---|
| 1479 |
|
|---|
| 1480 |
Pour les champs de date ou de date/heure, correspondance exacte de l'année. |
|---|
| 1481 |
Accepte une année sous la forme de quatre chiffres. |
|---|
| 1482 |
|
|---|
| 1483 |
Exemple:: |
|---|
| 1484 |
|
|---|
| 1485 |
Billet.objects.filter(date_publication__year=2005) |
|---|
| 1486 |
|
|---|
| 1487 |
Equivalent SQL:: |
|---|
| 1488 |
|
|---|
| 1489 |
SELECT ... WHERE EXTRACT('year' FROM date_publication) = '2005'; |
|---|
| 1490 |
|
|---|
| 1491 |
(La syntaxe SQL exacte est différente pour chaque moteur de base de données.) |
|---|
| 1492 |
|
|---|
| 1493 |
month |
|---|
| 1494 |
~~~~~ |
|---|
| 1495 |
|
|---|
| 1496 |
Pour les champs de date ou de date/heure, correspondance exacte du mois. |
|---|
| 1497 |
Accepte un entier de 1 (Janvier) à 12 (Décembre). |
|---|
| 1498 |
|
|---|
| 1499 |
Exemple:: |
|---|
| 1500 |
|
|---|
| 1501 |
Billet.objects.filter(date_publication__month=12) |
|---|
| 1502 |
|
|---|
| 1503 |
Equivalent SQL:: |
|---|
| 1504 |
|
|---|
| 1505 |
SELECT ... WHERE EXTRACT('month' FROM date_publication) = '12'; |
|---|
| 1506 |
|
|---|
| 1507 |
(La syntaxe SQL exacte est différente pour chaque moteur de base de données.) |
|---|
| 1508 |
|
|---|
| 1509 |
day |
|---|
| 1510 |
~~~ |
|---|
| 1511 |
|
|---|
| 1512 |
Pour les champs de date ou de date/heure, correspondance exacte du jour. |
|---|
| 1513 |
|
|---|
| 1514 |
Exemple:: |
|---|
| 1515 |
|
|---|
| 1516 |
Billet.objects.filter(date_publication__day=3) |
|---|
| 1517 |
|
|---|
| 1518 |
Equivalent SQL:: |
|---|
| 1519 |
|
|---|
| 1520 |
SELECT ... WHERE EXTRACT('day' FROM date_publication) = '3'; |
|---|
| 1521 |
|
|---|
| 1522 |
(La syntaxe SQL exacte est différente pour chaque moteur de base de données.) |
|---|
| 1523 |
|
|---|
| 1524 |
Notez que cela correspondera à toute données enregistrée ayant le troisiÚme jour du |
|---|
| 1525 |
mois comme date_publication, comme le 3 janvier, le 3 juillet, etc. |
|---|
| 1526 |
|
|---|
| 1527 |
isnull |
|---|
| 1528 |
~~~~~~ |
|---|
| 1529 |
|
|---|
| 1530 |
Accepte soit ``True`` soit ``False``, qui correspond aux requêtes SQL, |
|---|
| 1531 |
respectivement, ``IS NULL`` et ``IS NOT NULL``. |
|---|
| 1532 |
|
|---|
| 1533 |
Exemple:: |
|---|
| 1534 |
|
|---|
| 1535 |
Billet.objects.filter(date_publication__isnull=True) |
|---|
| 1536 |
|
|---|
| 1537 |
Equivalent SQL:: |
|---|
| 1538 |
|
|---|
| 1539 |
SELECT ... WHERE date_publication IS NULL; |
|---|
| 1540 |
|
|---|
| 1541 |
search |
|---|
| 1542 |
~~~~~~ |
|---|
| 1543 |
|
|---|
| 1544 |
Une recherche booléenne de texte intégral (full-text), ce qui permet de profiter |
|---|
| 1545 |
de l'indexation en texte intégral. Cela fonctionne de maniÚre similaire à |
|---|
| 1546 |
``contains`` mais beaucoup plus rapidement à cause de l'indexation texte intégral. |
|---|
| 1547 |
|
|---|
| 1548 |
Notez que cela n'est disponible que sous MySQL et qu'une manipulation directe |
|---|
| 1549 |
de la base de données est nécessaire pour ajouter l'index en texte intégral. |
|---|
| 1550 |
|
|---|
| 1551 |
regex |
|---|
| 1552 |
~~~~~ |
|---|
| 1553 |
|
|---|
| 1554 |
**Nouveau dans la version en développement de Django** |
|---|
| 1555 |
|
|---|
| 1556 |
Correspondance d'expression réguliÚre sensible à la casse. |
|---|
| 1557 |
|
|---|
| 1558 |
La syntaxe des expressions réguliÚres est celle utilisée par la base de données. |
|---|
| 1559 |
Dans le cas SQLite, qui ne supporte pas nativement une recherche par expression |
|---|
| 1560 |
réguliÚre, la syntaxe est celle du module Python ``re``. |
|---|
| 1561 |
|
|---|
| 1562 |
Exemple:: |
|---|
| 1563 |
|
|---|
| 1564 |
Billet.objects.get(title__regex=r'^(An?|The) +') |
|---|
| 1565 |
|
|---|
| 1566 |
Equivalents SQL:: |
|---|
| 1567 |
|
|---|
| 1568 |
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL |
|---|
| 1569 |
|
|---|
| 1570 |
SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'c'); -- Oracle |
|---|
| 1571 |
|
|---|
| 1572 |
SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL |
|---|
| 1573 |
|
|---|
| 1574 |
SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite |
|---|
| 1575 |
|
|---|
| 1576 |
L'utilisation de chaîne de caractÚres brutes (par exemple, ``r'foo'`` à la |
|---|
| 1577 |
place de ``'foo'``) pour la syntaxe des expressions réguliÚres est recommandée. |
|---|
| 1578 |
|
|---|
| 1579 |
iregex |
|---|
| 1580 |
~~~~~~ |
|---|
| 1581 |
|
|---|
| 1582 |
**Nouveau dans la version en développement de Django** |
|---|
| 1583 |
|
|---|
| 1584 |
Correspondance d'expression réguliÚre insensible à la casse. |
|---|
| 1585 |
|
|---|
| 1586 |
Exemple:: |
|---|
| 1587 |
|
|---|
| 1588 |
Billet.objects.get(title__iregex=r'^(an?|the) +') |
|---|
| 1589 |
|
|---|
| 1590 |
Equivalents SQL:: |
|---|
| 1591 |
|
|---|
| 1592 |
SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL |
|---|
| 1593 |
|
|---|
| 1594 |
SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle |
|---|
| 1595 |
|
|---|
| 1596 |
SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL |
|---|
| 1597 |
|
|---|
| 1598 |
SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite |
|---|
| 1599 |
|
|---|
| 1600 |
Les recherches par défaut sont exactes |
|---|
| 1601 |
-------------------------------------- |
|---|
| 1602 |
|
|---|
| 1603 |
Si vous ne fournissez pas un type de recherche, c'est-Ã -dire que votre mot-clef |
|---|
| 1604 |
argument ne contient pas de double soulignement, la recherche est présumée être |
|---|
| 1605 |
une recherche de type ``exact``. |
|---|
| 1606 |
|
|---|
| 1607 |
Par exemple, les deux déclarations suivantes sont équivalents:: |
|---|
| 1608 |
|
|---|
| 1609 |
Blog.objects.get(id__exact=14) # Forme explicite |
|---|
| 1610 |
Blog.objects.get(id=14) # __exact est implicite |
|---|
| 1611 |
|
|---|
| 1612 |
Ceci dans un but pratique, car les recherches ``exact`` sont les plus courantes. |
|---|
| 1613 |
|
|---|
| 1614 |
Le raccourci de recherche pk |
|---|
| 1615 |
---------------------------- |
|---|
| 1616 |
|
|---|
| 1617 |
Dans un but pratique, Django fourni un type de recherche ``pk``, |
|---|
| 1618 |
qui signifie "clef primaire" (primary_key). |
|---|
| 1619 |
|
|---|
| 1620 |
Dans l'exemple du modÚle ``Blog``, la clef primaire est le champ ``id``. Ces |
|---|
| 1621 |
trois déclarations sont donc équivalentes:: |
|---|
| 1622 |
|
|---|
| 1623 |
Blog.objects.get(id__exact=14) # Forme explicite |
|---|
| 1624 |
Blog.objects.get(id=14) # __exact est implicite |
|---|
| 1625 |
Blog.objects.get(pk=14) # pk implique id__exact |
|---|
| 1626 |
|
|---|
| 1627 |
L'utilisation de ``pk`` n'est pas limitée aux requêtes ``__exact``. N'importe |
|---|
| 1628 |
quel terme de requête peut être combiné avec ``pk`` pour effectuer une requête |
|---|
| 1629 |
sur la clef primaire du modÚle:: |
|---|
| 1630 |
|
|---|
| 1631 |
# RécupÚre les entrées de blog ayant pour id 1, 4 et 7 |
|---|
| 1632 |
Blog.objects.filter(pk__in=[1,4,7]) |
|---|
| 1633 |
# RécupÚre toutes les entrées ayant un id > 14 |
|---|
| 1634 |
Blog.objects.filter(pk__gt=14) |
|---|
| 1635 |
|
|---|
| 1636 |
Les recherches ``pk`` fonctionnent aussi lors de jointures croisées. Par exemple, |
|---|
| 1637 |
ces trois déclarations sont équivalentes:: |
|---|
| 1638 |
|
|---|
| 1639 |
Billet.objects.filter(blog__id__exact=3) # Forme explicite |
|---|
| 1640 |
Billet.objects.filter(blog__id=3) # __exact est implicite |
|---|
| 1641 |
Billet.objects.filter(blog__pk=3) # __pk implique __id__exact |
|---|
| 1642 |
|
|---|
| 1643 |
.. note:: |
|---|
| 1644 |
A cause de ce raccourci, vous ne pouvez pas avoir de champ nommé ``pk`` qui |
|---|
| 1645 |
ne soit pas la clef primaire du modÚle. Cela sera toujours remplacé par le nom |
|---|
| 1646 |
de la clef primaire du modÚle dans les requêtes. |
|---|
| 1647 |
|
|---|
| 1648 |
Les recherches étendues aux relations de liaison |
|---|
| 1649 |
------------------------------------------------ |
|---|
| 1650 |
|
|---|
| 1651 |
Django offre un moyen puissant et intuitif de "suivre" les relations de liaison |
|---|
| 1652 |
dans une recherche, en prenant soin des ``JOIN``\s SQL pour vous automatiquement, |
|---|
| 1653 |
en coulisse. Pour étendre une relation de liaison, utilisez simplement le nom |
|---|
| 1654 |
des champs apparentés à travers le modÚle, séparés par un double soulignement, |
|---|
| 1655 |
jusqu'à ce que vous arriviez au champ désiré. |
|---|
| 1656 |
|
|---|
| 1657 |
Cet exemple récupÚre tout les objets ``Billet`` ayant un champ ``Blog`` |
|---|
| 1658 |
dont le champ ``nom`` est ``'Beatles Blog'``:: |
|---|
| 1659 |
|
|---|
| 1660 |
Billet.objects.filter(blog__nom__exact='Beatles Blog') |
|---|
| 1661 |
|
|---|
| 1662 |
Cette étendue peut être aussi profonde que nécessaire. |
|---|
| 1663 |
|
|---|
| 1664 |
Cela fonctionne aussi en arriÚre. Pour référencer une relation de liaison "inversée", |
|---|
| 1665 |
utilisez juste le nom du modÚle en minuscule. |
|---|
| 1666 |
|
|---|
| 1667 |
Cette exemple récupÚre tous les objets ``Blog`` qui ont au moins un champ ``Billet`` |
|---|
| 1668 |
dont le champ ``titre`` contient ``'Lennon'``:: |
|---|
| 1669 |
|
|---|
| 1670 |
Blog.objects.filter(billet__titre__contains='Lennon') |
|---|
| 1671 |
|
|---|
| 1672 |
Parcours des relations multivaluées |
|---|
| 1673 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 1674 |
|
|---|
| 1675 |
**Nouveau dans la version en développement de Django** |
|---|
| 1676 |
|
|---|
| 1677 |
Lorsque l'on filtre un objet en se basant sur un champ ``ManyToManyField`` ou |
|---|
| 1678 |
sur un champ ``ForeignKeyField`` inversé, il y a deux maniÚres différentes de |
|---|
| 1679 |
trier qui peuvent vous intéresser. Prenons le cas de la relation ``Blog``/``Billet`` (``Blog`` |
|---|
| 1680 |
vers ``Billet`` est une relation un-vers-plusieurs). Il peut être intéressant |
|---|
| 1681 |
de retrouver les blogs qui ont un billet ayant pour titre *"Lennon"* et ayant |
|---|
| 1682 |
été publié aujourd'hui. Ou alors, on désire retrouver les blogs qui ont un billet |
|---|
| 1683 |
ayant pour titre *"Lennon"* et un autre billet ayant été publié aujourd'hui. |
|---|
| 1684 |
Puisque plusieurs billets sont associés à un seul ``Blog``, ces deux requêtes |
|---|
| 1685 |
sont possibles et ont un sens dans certaines situations. |
|---|
| 1686 |
|
|---|
| 1687 |
Le même type de situation se présente avec un champ ``ManyToManyField``. Par |
|---|
| 1688 |
exemple, si un ``Billet`` a un champ ``ManyToManyField`` nommé ``etiquettes`` et |
|---|
| 1689 |
que l'on désire trouver les billets ayant une étiquette nommé *"musique"* ou *"groupe"*, |
|---|
| 1690 |
ou que l'on désire utiliser un billet qui contient une étiquette avec le nom *"musique"* |
|---|
| 1691 |
et le statut de *"publique"*. |
|---|
| 1692 |
|
|---|
| 1693 |
Pour faire face à ces deux situations, Django utilise une maniÚre cohérente de |
|---|
| 1694 |
traitement pour les appels ``filter()`` et ``exclude()``. Tout ce qui appartient |
|---|
| 1695 |
à un unique appel ``filter()`` est appliqué simultanément pour filtrer les éléments |
|---|
| 1696 |
correspondants à ces exigences. Les appels successifs à ``filter()`` restreindront |
|---|
| 1697 |
l'ensemble des objets, mais pour une relation multivaluée, ils s'appliqueront à n'importe |
|---|
| 1698 |
quel objet lié au modÚle de base, pas nécessairement aux objets sélectionnés par |
|---|
| 1699 |
un appel précédent à ``filter()``. |
|---|
| 1700 |
|
|---|
| 1701 |
Cela peut paraître confus, donc voici un exemple pour clarifier. |
|---|
| 1702 |
Pour sélectionner tous les blogs qui ont un billet ayant pour titre |
|---|
| 1703 |
*"Lennon"* et ayant été publié aujourd'hui, on peut écrire:: |
|---|
| 1704 |
|
|---|
| 1705 |
Blog.objects.filter(billet__titre__contains='Lennon', |
|---|
| 1706 |
billet__date_publication=datetime.date.today()) |
|---|
| 1707 |
|
|---|
| 1708 |
Pour sélectionner les blogs qui ont un billet ayant pour titre *"Lennon"* |
|---|
| 1709 |
et, *en même temps*, un autre billet ayant été publié aujourd'hui, on peut écrire:: |
|---|
| 1710 |
|
|---|
| 1711 |
Blog.objects.filter(billet__titre__contains='Lennon').filter( |
|---|
| 1712 |
billet__date_publication=datetime.date.today()) |
|---|
| 1713 |
|
|---|
| 1714 |
Dans ce second exemple, le premier filtre restreint le ``QuerySet`` Ã tous |
|---|
| 1715 |
les blogs liés à ce type de billet. Le second filtre restreint l'ensemble des |
|---|
| 1716 |
blogs *supplémentaires* à ceux qui sont liés au second type de billet. Les billets |
|---|
| 1717 |
sélectionnés par le second filtre peuvent être ou non les mêmes que ceux du premier |
|---|
| 1718 |
filtre. Ici, on filtre les éléments ``Blog`` avec chaque filtre, et non les éléments |
|---|
| 1719 |
``Billet``. |
|---|
| 1720 |
|
|---|
| 1721 |
Tout ce comportement s'applique aussi à ``exclude()`` : toutes les conditions d'une |
|---|
| 1722 |
seule déclaration ``exclude()`` sont appliquées à une seule instance (si ces conditions |
|---|
| 1723 |
concernent la même relation multivaluée). Les conditions dans les appels à |
|---|
| 1724 |
``filter()`` ou ``exclude()`` suivants, qui feront référence à la même relation, |
|---|
| 1725 |
peuvent filtrer sur différents objets liés. |
|---|
| 1726 |
|
|---|
| 1727 |
Ãchapper les symboles pourcent et soulignement dans une déclaration LIKE |
|---|
| 1728 |
------------------------------------------------------------------------ |
|---|
| 1729 |
|
|---|
| 1730 |
Les recherches de champ équivalentes aux déclarations ``LIKE`` de SQL (``iexact``, |
|---|
| 1731 |
``contains``, ``icontains``, ``startswith``, ``istartswith``, ``endswith`` |
|---|
| 1732 |
and ``iendswith``) échapperont automatiquement les deux caractÚres spéciaux |
|---|
| 1733 |
utilisés dans la déclaration ``LIKE``, le pourcent et le soulignement. |
|---|
| 1734 |
(Dans une déclaration ``LIKE``, le symbole pourcent représente un joker pour |
|---|
| 1735 |
plusieurs caractÚres et un soulignement représente un joker pour un unique |
|---|
| 1736 |
caractÚre.) |
|---|
| 1737 |
|
|---|
| 1738 |
Cela signifie que les choses fonctionnent intuitivement, il n'y a pas |
|---|
| 1739 |
de faille dans l'abstraction. Par exemple, pour récupérer toutes les entrées |
|---|
| 1740 |
contenant un signe pourcent, utilisez simplement le signe pourcent comme |
|---|
| 1741 |
tout autre caractÚre:: |
|---|
| 1742 |
|
|---|
| 1743 |
Billet.objects.filter(titre__contains='%') |
|---|
| 1744 |
|
|---|
| 1745 |
Django place les guillemets pour vous. La requête SQL résultante devrait ressembler |
|---|
| 1746 |
à quelque chose comme ca:: |
|---|
| 1747 |
|
|---|
| 1748 |
SELECT ... WHERE titre LIKE '%\%%'; |
|---|
| 1749 |
|
|---|
| 1750 |
Les soulignements fonctionnent pareillement. Les symboles pourcent et soulignement |
|---|
| 1751 |
sont gérés pour vous de maniÚre transparante. |
|---|
| 1752 |
|
|---|
| 1753 |
Les QuerySets et leurs caches |
|---|
| 1754 |
----------------------------- |
|---|
| 1755 |
|
|---|
| 1756 |
Chaque ``QuerySet`` contient un cache afin de minimiser le nombre d'accÚs à la base |
|---|
| 1757 |
de données. Il est important de comprendre leurs fonctionnement pour écrire |
|---|
| 1758 |
le code le plus efficace. |
|---|
| 1759 |
|
|---|
| 1760 |
Dans tout nouveau ``QuerySet``, le cache est vide. A la premiÚre évaluation |
|---|
| 1761 |
de celui-ci, une requête est exécutée sur la base, Django enregistre le |
|---|
| 1762 |
résultat de la requête dans le cache du ``QuerySet`` et retourne le résultat |
|---|
| 1763 |
explicitement demandé (par exemple, le prochain élément, si le ``QuerySet`` |
|---|
| 1764 |
est itéré). Les évaluations futures du ``QuerySet`` réutiliseront les résultats |
|---|
| 1765 |
contenus dans le cache. |
|---|
| 1766 |
|
|---|
| 1767 |
Il est important de se souvenir du comportement du cache, car cela peut vous |
|---|
| 1768 |
jouer un mauvais tour si vous n'utilisez pas le ``QuerySet`` correctement. |
|---|
| 1769 |
Par exemple, les lignes suivantes vont créer deux ``QuerySet``, les évaluer, |
|---|
| 1770 |
et les jeter:: |
|---|
| 1771 |
|
|---|
| 1772 |
print [e.titre for e in Billet.objects.all()] |
|---|
| 1773 |
print [e.date_publication for e in Billet.objects.all()] |
|---|
| 1774 |
|
|---|
| 1775 |
Cela signifie que la même requête sera exécutée deux fois, doublant ainsi la |
|---|
| 1776 |
charge de la base de données. Aussi, il y a une possibilité que les deux listes |
|---|
| 1777 |
n'incluent pas les mêmes enregistrements de la base, si un enregistrement |
|---|
| 1778 |
``Billet`` a été ajouté ou supprimé entre les deux requêtes. |
|---|
| 1779 |
|
|---|
| 1780 |
Pour éviter ce problÚme, enregistrez simplement le ``QuerySet`` et réutilisez le:: |
|---|
| 1781 |
|
|---|
| 1782 |
queryset = Billet.objects.all() |
|---|
| 1783 |
print [p.titre for p in queryset] # Evalue le QuerySet. |
|---|
| 1784 |
print [p.date_publication for p in queryset] # Réutilise le cache de l'évaluation précédente. |
|---|
| 1785 |
|
|---|
| 1786 |
Comparaison d'objets |
|---|
| 1787 |
==================== |
|---|
| 1788 |
|
|---|
| 1789 |
Pour comparer deux instances d'un modÚle, utilisez simplement l'opérateur standard |
|---|
| 1790 |
de comparaison de Python, le signe double égal: ``==``. En coulisse, ceci comparera |
|---|
| 1791 |
les clefs primaires des deux instances. |
|---|
| 1792 |
|
|---|
| 1793 |
Appliqué à l'exemple ``Billet`` ci-dessus, les deux déclarations suivantes sont équivalentes:: |
|---|
| 1794 |
|
|---|
| 1795 |
some_Billet == other_Billet |
|---|
| 1796 |
some_Billet.id == other_Billet.id |
|---|
| 1797 |
|
|---|
| 1798 |
Si la clef primaire du modÚle n'est pas appelée ``id``, pas de problÚme. |
|---|
| 1799 |
La comparaison s'effectuera toujours selon la clef primaire, quelque soit |
|---|
| 1800 |
son nom. Par exemple, si la clef primaire d'un modÚle est appelée ``nom``, |
|---|
| 1801 |
ces deux déclarations sont équivalentes:: |
|---|
| 1802 |
|
|---|
| 1803 |
some_obj == other_obj |
|---|
| 1804 |
some_obj.nom == other_obj.nom |
|---|
| 1805 |
|
|---|
| 1806 |
Recherche complexe avec les objets Q |
|---|
| 1807 |
==================================== |
|---|
| 1808 |
|
|---|
| 1809 |
Les requêtes avec argument mot-clef -- dans ``filter()``, etc. -- sont assemblées |
|---|
| 1810 |
à l'aide d'un "ET" (AND). Si vous désirez exécuter des requêtes plus complexes |
|---|
| 1811 |
(par exemple, des requêtes avec "OU" (OR)), vous pouvez utilisez les objets ``Q``. |
|---|
| 1812 |
|
|---|
| 1813 |
Un objet ``Q`` (``django.db.models.Q``) est un objet utilisé pour encapsuler une |
|---|
| 1814 |
collection d'argument mot-clef. Ces arguments mot-clé sont spécifiés comme pour la recherche |
|---|
| 1815 |
sur champs ci-dessus. |
|---|
| 1816 |
|
|---|
| 1817 |
Par exemple, cet objet ``Q`` encapsule une unique requête ``LIKE``:: |
|---|
| 1818 |
|
|---|
| 1819 |
Q(question__startswith='What') |
|---|
| 1820 |
|
|---|
| 1821 |
Les objets ``Q`` peuvent être combinés en utilisant les opérateurs ``&`` et |
|---|
| 1822 |
``|``. Lorsqu'un opérateur est utilisé sur deux objets ``Q``, il retourne |
|---|
| 1823 |
un nouvel objet ``Q``. |
|---|
| 1824 |
|
|---|
| 1825 |
Par exemple, cette déclaration retourne un unique objet ``Q`` représentant |
|---|
| 1826 |
l'union des deux requêtes ``"question__startswith"``:: |
|---|
| 1827 |
|
|---|
| 1828 |
Q(question__startswith='Qui') | Q(question__startswith='Quoi') |
|---|
| 1829 |
|
|---|
| 1830 |
Ce qui est équivalent à la clause ``WHERE`` SQL suivante:: |
|---|
| 1831 |
|
|---|
| 1832 |
WHERE question LIKE 'Qui%' OR question LIKE 'Quoi%' |
|---|
| 1833 |
|
|---|
| 1834 |
Vous pouvez composer les déclarations d'une complexité arbitraire en combinant |
|---|
| 1835 |
les objets ``Q`` avec les opérateurs ``&`` et ``|``. Vous pouvez aussi |
|---|
| 1836 |
utiliser des parenthÚses de regroupement. |
|---|
| 1837 |
|
|---|
| 1838 |
**Nouveau dans la version en développement de Django:** Les objets``Q`` peuvent aussi |
|---|
| 1839 |
être niés en utilisant l'opérateur ``~``, permettant ainsi aux recherches combinées d'être |
|---|
| 1840 |
combinées avec des requêtes normales et niées (``NOT``):: |
|---|
| 1841 |
|
|---|
| 1842 |
Q(question__startswith='Who') | ~Q(date_publication__year=2005) |
|---|
| 1843 |
|
|---|
| 1844 |
Chaque fonction de recherche qui prends des arguments mots-clefs (par exemple, |
|---|
| 1845 |
``filter()``, ``exclude()``, ``get()``) peut aussi avoir un ou plusieurs objets |
|---|
| 1846 |
``Q`` en tant qu'argument positionnel (non-nommé). Si vous fournissez plusieurs |
|---|
| 1847 |
arguments objet ``Q`` à la fonction de recherche, les arguments seront assemblés |
|---|
| 1848 |
à l'aide d'un "ET" (AND). Par exemple:: |
|---|
| 1849 |
|
|---|
| 1850 |
Sondage.objects.get( |
|---|
| 1851 |
Q(question__startswith='Qui'), |
|---|
| 1852 |
Q(date_publication=date(2005, 5, 2)) | Q(date_publication=date(2005, 5, 6)) |
|---|
| 1853 |
) |
|---|
| 1854 |
|
|---|
| 1855 |
... traduit grossiÚrement en requête SQL:: |
|---|
| 1856 |
|
|---|
| 1857 |
SELECT * from sondage WHERE LIKE 'Qui%' |
|---|
| 1858 |
AND (date_publication = '2005-05-02' OR date_publication = '2005-05-06') |
|---|
| 1859 |
|
|---|
| 1860 |
Les fonctions de recherches peuvent mixer l'utilisation des objets ``Q`` et des |
|---|
| 1861 |
arguments mots-clefs. Tous les arguments fournis à une fonction de recherche (qu'ils |
|---|
| 1862 |
soient des arguments mots-clefs ou des objets ``Q``) sont assemblés par un "ET" ensemble. |
|---|
| 1863 |
Cependant, si un objet ``Q`` est fourni, il doit précéder la définition de tout argument |
|---|
| 1864 |
mot-clef. Par exemple:: |
|---|
| 1865 |
|
|---|
| 1866 |
Sondage.objects.get( |
|---|
| 1867 |
Q(date_publication=date(2005, 5, 2)) | Q(date_publication=date(2005, 5, 6)), |
|---|
| 1868 |
question__startswith='Qui') |
|---|
| 1869 |
|
|---|
| 1870 |
... serait une requête valide, équivalente à l'exemple précédent. Mais:: |
|---|
| 1871 |
|
|---|
| 1872 |
# REQUETE INVALIDE |
|---|
| 1873 |
Sondage.objects.get( |
|---|
| 1874 |
question__startswith='Qui', |
|---|
| 1875 |
Q(date_publication=date(2005, 5, 2)) | Q(date_publication=date(2005, 5, 6))) |
|---|
| 1876 |
|
|---|
| 1877 |
... ne serait pas valide. |
|---|
| 1878 |
|
|---|
| 1879 |
Voir la `page d'exemples de recherche par union`_ pour plus d'exemples. |
|---|
| 1880 |
|
|---|
| 1881 |
.. _page d'exemples de recherche par union: ../models/or_lookups/ |
|---|
| 1882 |
|
|---|
| 1883 |
Objets apparentés |
|---|
| 1884 |
================= |
|---|
| 1885 |
|
|---|
| 1886 |
Lorsque vous définissez une relation de liaison dans un modÚle (c-à -d, un champ |
|---|
| 1887 |
``ForeignKey``, ``OneToOneField``, ou ``ManyToManyField``), les instances de ce |
|---|
| 1888 |
modÚle auront une API pratique pour accéder aux objets apparentés. |
|---|
| 1889 |
|
|---|
| 1890 |
Appliqué aux modÚles présents en haut de cette page, par exemple, un objet |
|---|
| 1891 |
``Billet`` nommé ``e`` peut accéder à son objet ``Blog`` associé en utilisant |
|---|
| 1892 |
l'attribut ``blog``: ``e.blog``. |
|---|
| 1893 |
|
|---|
| 1894 |
(En coulisse, cette fonctionnalité est implémentée à l'aide des `descripteurs Python`_. |
|---|
| 1895 |
Cela ne doit pas être trÚs important pour vous, mais nous en parlons pour les curieux.) |
|---|
| 1896 |
|
|---|
| 1897 |
Django crée aussi des accesseurs d'API pour les "autres" cÎtés de la relation de liaison, |
|---|
| 1898 |
comme le lien entre l'objet apparenté et le modÚle définissant la relation de liaison. |
|---|
| 1899 |
Par exemple, un objet ``Blog`` nommé ``b`` a accÚs à une liste de tous les objets |
|---|
| 1900 |
``Billet`` liés via l'attribut ``Billet_set``: ``b.Billet_set.all()``. |
|---|
| 1901 |
|
|---|
| 1902 |
Tous les exemples de cette section utilisent les modÚles ``Blog``, ``Author`` et |
|---|
| 1903 |
``Billet`` définis en haut de cette page. |
|---|
| 1904 |
|
|---|
| 1905 |
.. _descripteurs Python: http://users.rcn.com/python/download/Descriptor.htm |
|---|
| 1906 |
|
|---|
| 1907 |
Les relations un-vers-plusieurs |
|---|
| 1908 |
------------------------------- |
|---|
| 1909 |
|
|---|
| 1910 |
En avant |
|---|
| 1911 |
~~~~~~~~ |
|---|
| 1912 |
|
|---|
| 1913 |
Si un modÚle a un champ ``ForeignKey``, les instances de ce modÚles auront accÚs |
|---|
| 1914 |
à l'objet apparenté (étranger) via un simple attribut du modÚle. |
|---|
| 1915 |
|
|---|
| 1916 |
Exemple:: |
|---|
| 1917 |
|
|---|
| 1918 |
e = Billet.objects.get(id=2) |
|---|
| 1919 |
e.blog # Retourne l'objet Blog apparenté. |
|---|
| 1920 |
|
|---|
| 1921 |
On peut accéder et modifier des données via un attribut clef étrangÚre. Comme |
|---|
| 1922 |
on peut s'y attendre, les changements sur la clé étrangÚre ne sont enregistrés |
|---|
| 1923 |
que lors de l'appel à ``save()``. |
|---|
| 1924 |
Exemple:: |
|---|
| 1925 |
|
|---|
| 1926 |
e = Billet.objects.get(id=2) |
|---|
| 1927 |
e.blog = un_autre_blog |
|---|
| 1928 |
e.save() |
|---|
| 1929 |
|
|---|
| 1930 |
Si un champ ``ForeignKey`` a l'option ``null=True`` (c-Ã -d, s'il accepte |
|---|
| 1931 |
les valeurs ``NULL``), on peut lui assigner la valeur ``None``. Exemple:: |
|---|
| 1932 |
|
|---|
| 1933 |
e = Billet.objects.get(id=2) |
|---|
| 1934 |
e.blog = None |
|---|
| 1935 |
e.save() # "UPDATE blog_billet SET blog_id = NULL ...;" |
|---|
| 1936 |
|
|---|
| 1937 |
L'accÚs en avant à une relation un-vers-plusieurs met en cache l'objet |
|---|
| 1938 |
concerné par la relation. Les accÚs ultérieurs à la clé étrangÚre sur le même |
|---|
| 1939 |
objet utilisent le cache. Exemple:: |
|---|
| 1940 |
|
|---|
| 1941 |
e = Billet.objects.get(id=2) |
|---|
| 1942 |
print e.blog # Interroge la base de données pour retrouver le Blog associé. |
|---|
| 1943 |
print e.blog # N'interroge pas la base de données. Utilise la version en cache. |
|---|
| 1944 |
|
|---|
| 1945 |
On peut noter que la méthode ``select_related()`` du ``QuerySet`` remplit récursivement |
|---|
| 1946 |
le cache de toutes les relations un-vers-plusieurs en avance. Exemple:: |
|---|
| 1947 |
|
|---|
| 1948 |
e = Billet.objects.select_related().get(id=2) |
|---|
| 1949 |
print e.blog # N'interroge pas la base de données. Utilise la version en cache. |
|---|
| 1950 |
print e.blog # N'interroge pas la base de données. Utilise la version en cache. |
|---|
| 1951 |
|
|---|
| 1952 |
La méthode ``select_related()`` est documentée dans la section `Les méthodes des |
|---|
| 1953 |
QuerySets qui renvoient d'autres QuerySets`_ ci-dessus. |
|---|
| 1954 |
|
|---|
| 1955 |
En arriÚre |
|---|
| 1956 |
~~~~~~~~~~ |
|---|
| 1957 |
|
|---|
| 1958 |
Si un modÚle possÚde un champ ``ForeignKey``, les instances du modÚle de la clé |
|---|
| 1959 |
étrangÚre auront accÚs à un ``Manager`` qui retourne toutes les instances du premier |
|---|
| 1960 |
modÚle. Par défaut, ce ``Manager`` est nommé ``FOO_set``, où ``FOO`` est le nom |
|---|
| 1961 |
du modÚle source, en minuscules. Ce ``Manager`` retourne des ``QuerySets``, qui |
|---|
| 1962 |
peuvent être filtrés et manipulés comme décrit dans la section `Récupération des objets`_ |
|---|
| 1963 |
ci-dessus. |
|---|
| 1964 |
|
|---|
| 1965 |
Exemple:: |
|---|
| 1966 |
|
|---|
| 1967 |
b = Blog.objects.get(id=1) |
|---|
| 1968 |
b.billet_set.all() # Retourne tous les objets Billet reliés au Blog. |
|---|
| 1969 |
|
|---|
| 1970 |
# b.entry_set est un Manager qui retourne des QuerySets. |
|---|
| 1971 |
b.billet_set.filter(titre__contains='Lennon') |
|---|
| 1972 |
b.billet_set.count() |
|---|
| 1973 |
|
|---|
| 1974 |
Il est possible de redéfinir le nom de ``FOO_set`` en paramétrant |
|---|
| 1975 |
l'attribut ``related_name`` dans la définition de ``ForeignKey()``. Par exemple, |
|---|
| 1976 |
si le modÚle ``Billet`` était modifié par ``blog = ForeignKey(Blog, related_name='billets')``, |
|---|
| 1977 |
le code de l'exemple ci-dessus ressemblerait à ceci:: |
|---|
| 1978 |
|
|---|
| 1979 |
b = Blog.objects.get(id=1) |
|---|
| 1980 |
b.billets.all() # retourne tous les objets Billet reliés au Blog. |
|---|
| 1981 |
|
|---|
| 1982 |
# b.entry_set est un Manager qui retourne des QuerySets. |
|---|
| 1983 |
b.billets.filter(titre__contains='Lennon') |
|---|
| 1984 |
b.billets.count() |
|---|
| 1985 |
|
|---|
| 1986 |
Il est impossible d'accéder au ``Manager`` d'une clé étrangÚre en arriÚre à partir |
|---|
| 1987 |
de la classe. L'accÚs doit se faire à partir d'une instance. Exemple:: |
|---|
| 1988 |
|
|---|
| 1989 |
Blog.billet_set # lÚve l'exception AttributeError: "Manager must be accessed via instance". |
|---|
| 1990 |
|
|---|
| 1991 |
En plus des méthodes du ``QuerySet`` définies dans la section `Récupération des objets`_ |
|---|
| 1992 |
ci-dessus, le ``Manager`` de ``ForeignKey`` possÚde ces méthodes additionnelles: |
|---|
| 1993 |
|
|---|
| 1994 |
* ``add(obj1, obj2, ...)``: Ajoute l'objet du modÚle spécifié à l'ensemble |
|---|
| 1995 |
des objets reliés. |
|---|
| 1996 |
|
|---|
| 1997 |
Exemple:: |
|---|
| 1998 |
|
|---|
| 1999 |
b = Blog.objects.get(id=1) |
|---|
| 2000 |
e = Billet.objects.get(id=234) |
|---|
| 2001 |
b.billet_set.add(e) # associe le Billet e avec le Blog b |
|---|
| 2002 |
|
|---|
| 2003 |
* ``create(**kwargs)``: Crée un nouvel objet, l'enregistre et l'ajoute à l'ensemble des |
|---|
| 2004 |
objets reliés. Renvoie l'objet nouvellement créé. |
|---|
| 2005 |
|
|---|
| 2006 |
Exemple:: |
|---|
| 2007 |
|
|---|
| 2008 |
b = Blog.objects.get(id=1) |
|---|
| 2009 |
e = b.billet_set.create(titre='Bonjour', texte='Coucou', date_publication=datetime.date(2005, 1, 1)) |
|---|
| 2010 |
# Pas besoin d'appeler e.save() à cet endroit, le billet a déjà été enregistré. |
|---|
| 2011 |
|
|---|
| 2012 |
Ceci est équivalent à (mais plus simple que):: |
|---|
| 2013 |
|
|---|
| 2014 |
b = Blog.objects.get(id=1) |
|---|
| 2015 |
e = Billet(blog=b, titre='Bonjour', texte='Coucou', date_publication=datetime.date(2005, 1, 1)) |
|---|
| 2016 |
e.save() |
|---|
| 2017 |
|
|---|
| 2018 |
Notez qu'il n'est pas nécessaire de préciser l'argument mot-clef du modÚle |
|---|
| 2019 |
définissant la relation. Dans l'exemple précédent, on ne passe pas le paramÚtre |
|---|
| 2020 |
``blog`` à ``create()``. Django trouve lui-même que le champ ``blog`` du |
|---|
| 2021 |
nouvel objet ``Billet`` doit prendre la valeur ``b``. |
|---|
| 2022 |
|
|---|
| 2023 |
* ``remove(obj1, obj2, ...)``: Retire les objets du modÚle spécifié de l'ensemble |
|---|
| 2024 |
des objets reliés. |
|---|
| 2025 |
|
|---|
| 2026 |
Exemple:: |
|---|
| 2027 |
|
|---|
| 2028 |
b = Blog.objects.get(id=1) |
|---|
| 2029 |
e = Billet.objects.get(id=234) |
|---|
| 2030 |
b.billet_set.remove(e) # Désassocie le Billet e du Blog b. |
|---|
| 2031 |
|
|---|
| 2032 |
Afin d'éviter l'inconsistance de la base de données, cette méthode existe |
|---|
| 2033 |
seulement sur les objets ``ForeignKey`` où ``null=True``. Si le champ |
|---|
| 2034 |
relié ne peut pas prendre la valeur ``None`` (``NULL``), alors un objet |
|---|
| 2035 |
ne peut peut pas être retiré d'une relation sans être ajouté à une autre. |
|---|
| 2036 |
Dans l'exemple ci-dessus, retirer ``e`` de ``b.billet_set`` est équivalent |
|---|
| 2037 |
à écrire ``e.blog = None``, et puisque l'attribut ``ForeignKey`` ``blog`` n'a |
|---|
| 2038 |
pas ``null=True``, cela est invalide. |
|---|
| 2039 |
|
|---|
| 2040 |
* ``clear()``: Retire tous les objets de l'ensemble des objets reliés. |
|---|
| 2041 |
|
|---|
| 2042 |
Exemple:: |
|---|
| 2043 |
|
|---|
| 2044 |
b = Blog.objects.get(id=1) |
|---|
| 2045 |
b.billet_set.clear() |
|---|
| 2046 |
|
|---|
| 2047 |
Noter que cela ne supprime pas les objets reliés, cela les dissocie juste. |
|---|
| 2048 |
|
|---|
| 2049 |
Tout comme ``remove()``, ``clear()`` est uniquement disponible sur les champs |
|---|
| 2050 |
``ForeignKey`` où ``null=True``. |
|---|
| 2051 |
|
|---|
| 2052 |
Pour affecter les membres d'un ensemble relié d'un seul coup, il suffit de lui affecter |
|---|
| 2053 |
un objet itérable. Exemple:: |
|---|
| 2054 |
|
|---|
| 2055 |
b = Blog.objects.get(id=1) |
|---|
| 2056 |
b.billet_set = [e1, e2] |
|---|
| 2057 |
|
|---|
| 2058 |
Si la méthode ``clear()`` est disponible, tous les objets pré-existant |
|---|
| 2059 |
seront retirés de ``billet_set`` avant d'ajouter tous les objets contenus |
|---|
| 2060 |
dans l'itérable (dans ce cas, une liste). Si la méthode ``clear()`` n'est |
|---|
| 2061 |
*pas* disponible, tous les objets contenus dans l'itérable seront ajoutés |
|---|
| 2062 |
sans retirer les éléments existants. |
|---|
| 2063 |
|
|---|
| 2064 |
Chaque opération "inverse" décrite dans cette section a un effet immédiat sur |
|---|
| 2065 |
la base de données. Chaque ajout, création et suppression est immédiatement |
|---|
| 2066 |
et automatiquement enregistré dans la base de données. |
|---|
| 2067 |
|
|---|
| 2068 |
Les relations un-vers-un |
|---|
| 2069 |
------------------------ |
|---|
| 2070 |
|
|---|
| 2071 |
Les relations un-vers-un (one-to-one) sont vraiment similaires |
|---|
| 2072 |
au relations un-vers-plusieurs. |
|---|
| 2073 |
Si l'on définit un champ ``OneToOneField`` dans un modÚle, les instances |
|---|
| 2074 |
de ce modÚle auront accÚs à l'objet relié via un simple attribut du modÚle. |
|---|
| 2075 |
|
|---|
| 2076 |
Par exemple:: |
|---|
| 2077 |
|
|---|
| 2078 |
class BilletDetail(models.Model): |
|---|
| 2079 |
billet = models.OneToOneField(Billet) |
|---|
| 2080 |
details = models.TextField() |
|---|
| 2081 |
|
|---|
| 2082 |
bd = BilletDetail.objects.get(id=2) |
|---|
| 2083 |
bd.entry # renvoie l'objet Billet relié. |
|---|
| 2084 |
|
|---|
| 2085 |
Les différences apparaissent dans les requêtes inversés. Le modÚle relié dans |
|---|
| 2086 |
une relation un-vers-un a aussi accÚs à un objet ``Manager``. Cependant, ce |
|---|
| 2087 |
``Manager`` représente un unique objet, au lieu d'une collection d'objets:: |
|---|
| 2088 |
|
|---|
| 2089 |
b = Billet.objects.get(id=2) |
|---|
| 2090 |
b.billetdetail # renvoie l'objet BilletDetail relié. |
|---|
| 2091 |
|
|---|
| 2092 |
Si aucun objet a été affecté à cette relation, Django lÚvera une exception |
|---|
| 2093 |
``DoesNotExist``. |
|---|
| 2094 |
|
|---|
| 2095 |
Les instances peuvent être affectées à la relation inverse de la même maniÚre |
|---|
| 2096 |
que l'affection dans une relation en avant:: |
|---|
| 2097 |
|
|---|
| 2098 |
b.entrydetail = bd |
|---|
| 2099 |
|
|---|
| 2100 |
Les relations plusieurs-vers-plusieurs |
|---|
| 2101 |
-------------------------------------- |
|---|
| 2102 |
|
|---|
| 2103 |
Les deux extrémités d'une relation plusieurs-vers-plusieurs ont automatiquement |
|---|
| 2104 |
accÚs à l'autre extrémités grâce à l'API. L'API fonctionne comme la relation |
|---|
| 2105 |
un-vers-plusieurs en arriÚre. Voir `En arriÚre`_ ci-dessus. |
|---|
| 2106 |
|
|---|
| 2107 |
La seule différence réside dans le nommage de l'attribut: le modÚle qui définit |
|---|
| 2108 |
le champ ``ManyToManyField`` utilise le nom du champ de l'attribut lui-même, tandis |
|---|
| 2109 |
que le modÚle "inversé" utilise le nom du modÚle d'origine en minuscule, suivi |
|---|
| 2110 |
de ``'_set'`` (tout comme les relations un-vers-plusieurs inversées). |
|---|
| 2111 |
|
|---|
| 2112 |
Un exemple permet une meilleure compréhension:: |
|---|
| 2113 |
|
|---|
| 2114 |
b = Billet.objects.get(id=3) |
|---|
| 2115 |
b.auteurs.all() # renvoie tous les objets Auteur associés à ce Billet |
|---|
| 2116 |
b.auteurs.count() |
|---|
| 2117 |
b.auteurs.filter(nom__contains='John') |
|---|
| 2118 |
|
|---|
| 2119 |
a = Auteur.objects.get(id=5) |
|---|
| 2120 |
a.billet_set.all() # renvoie tous les objets Billet associés à cet Auteur |
|---|
| 2121 |
|
|---|
| 2122 |
Comme ``ForeignKey``, ``ManyToManyField`` accepte le paramÚtre ``related_name``. |
|---|
| 2123 |
Dans l'exemple précédent, si le champ ``ManyToManyField`` de ``Billet`` avait |
|---|
| 2124 |
spécifié ``related_name='billets'``, alors chaque instance du modÚle ``Auteur`` |
|---|
| 2125 |
aurait eu un attribut ``billets`` Ã la place de ``bilet_set``. |
|---|
| 2126 |
|
|---|
| 2127 |
Comment les relations en arriÚre sont-elles possible ? |
|---|
| 2128 |
------------------------------------------------------ |
|---|
| 2129 |
|
|---|
| 2130 |
Les autres ORM (Object-relational mapping) nécessite une définition de la |
|---|
| 2131 |
relation au deux extrémités. Les développeurs Django pensent que ceci |
|---|
| 2132 |
constitue une violation du principe DRY (Don't Repeat Yourself), donc Django |
|---|
| 2133 |
ne nécessite qu'une définition à une extrémité. |
|---|
| 2134 |
|
|---|
| 2135 |
Mais comment cela est possible, étant donné que la classe modÚle ne connaît |
|---|
| 2136 |
pas les autres classes modÚles reliés avant que celles-ci ne soient chargées ? |
|---|
| 2137 |
|
|---|
| 2138 |
La réponse se trouve dans le paramÚtre ``INSTALLED_APPS``. La premiÚre fois |
|---|
| 2139 |
qu'un modÚle est chargé, Django itÚre sur tous les modÚles de ``INSTALLED_APPS`` |
|---|
| 2140 |
et crée les relations en arriÚre nécessaires en mémoire. Essentiellement, |
|---|
| 2141 |
une des fonctions de ``INSTALLED_APPS`` est d'indiquer à Django le domaine |
|---|
| 2142 |
de modÚle. |
|---|
| 2143 |
|
|---|
| 2144 |
Requêtes sur des objets reliés |
|---|
| 2145 |
------------------------------ |
|---|
| 2146 |
|
|---|
| 2147 |
Les requêtes impliquant des objets reliés suivent les même rÚgles que les requêtes |
|---|
| 2148 |
impliquant des champs à valeurs normals. Lorsqu'on spécifie la valeur de correspondance |
|---|
| 2149 |
pour une requête, on utilise soit une instance de l'objet lui-même, soit la valeur |
|---|
| 2150 |
de la clef primaire pour l'objet. |
|---|
| 2151 |
|
|---|
| 2152 |
Par exemple, s'il existait un objet ``b`` de type ``Blog`` avec ``id=5``, |
|---|
| 2153 |
les trois requêtes suivantes seraient identiques:: |
|---|
| 2154 |
|
|---|
| 2155 |
Billet.objects.filter(blog=b) # requête utilisant l'instance de l'objet |
|---|
| 2156 |
Billet.objects.filter(blog=b.id) # requête utilisant l'id de l'objet |
|---|
| 2157 |
Billet.objects.filter(blog=5) # requête utilisant l'id directement |
|---|
| 2158 |
|
|---|
| 2159 |
Suppression d'objets |
|---|
| 2160 |
==================== |
|---|
| 2161 |
|
|---|
| 2162 |
La méthode de suppression est commodément nommé ``delete()``. Cette méthode |
|---|
| 2163 |
supprime l'objet immédiatement et ne renvoie pas de valeur. Exemple:: |
|---|
| 2164 |
|
|---|
| 2165 |
b.delete() |
|---|
| 2166 |
|
|---|
| 2167 |
Il est aussi possible de supprimer des objets par lots. Chaque ``QuerySet`` |
|---|
| 2168 |
possÚde une méthode ``delete()``, qui supprime tous les membres de ce |
|---|
| 2169 |
``QuerySet``. |
|---|
| 2170 |
|
|---|
| 2171 |
Par exemple, cela supprime tous les objets ``Billet`` ayant une ``date_publication`` |
|---|
| 2172 |
datée de 2005:: |
|---|
| 2173 |
|
|---|
| 2174 |
Billet.objects.filter(date_publication__year=2005).delete() |
|---|
| 2175 |
|
|---|
| 2176 |
Lorsque Django supprime un objet, il simule le comportement de la contrainte |
|---|
| 2177 |
SQL ``ON DELETE CASCADE``. En d'autres termes, chaque objets qui |
|---|
| 2178 |
possÚde une clé étrangÚre pointant sur l'objet a supprimer, sera |
|---|
| 2179 |
supprimé lui aussi. Par exemple:: |
|---|
| 2180 |
|
|---|
| 2181 |
b = Blog.objects.get(pk=1) |
|---|
| 2182 |
# Cela supprime le Blog et tous les objets Billet associés |
|---|
| 2183 |
b.delete() |
|---|
| 2184 |
|
|---|
| 2185 |
Notez que ``delete()`` est la seule méthode de ``QuerySet`` qui n'est pas apparente |
|---|
| 2186 |
au ``Manager``. C'est un méchanisme de sécurité pour éviter de demander accidentellement |
|---|
| 2187 |
``Entry.objects.delete()`` et de supprimer *tous* les billets. Si l'on veut |
|---|
| 2188 |
*absolument* supprimer tous les objets, il faut le faire explicitement sur tous |
|---|
| 2189 |
l'ensemble:: |
|---|
| 2190 |
|
|---|
| 2191 |
Billet.objects.all().delete() |
|---|
| 2192 |
|
|---|
| 2193 |
Mettre à jour plusieurs objets d'un coup |
|---|
| 2194 |
======================================== |
|---|
| 2195 |
|
|---|
| 2196 |
**Nouveau dans la version de développement** |
|---|
| 2197 |
|
|---|
| 2198 |
Parfois on désire affecter à un champ une valeur particuliÚre pour tous les objets |
|---|
| 2199 |
d'un ``QuerySet``. Il est possible de le faire en utilisant la méthode ``update()``. |
|---|
| 2200 |
Par exemple:: |
|---|
| 2201 |
|
|---|
| 2202 |
# mise à jour de tous les titres ayant une date de publication datée de 2007. |
|---|
| 2203 |
Billet.objects.filter(date_publication__year=2007).update(titre='Toujours la même chose') |
|---|
| 2204 |
|
|---|
| 2205 |
Il est uniquement possible de modifier des champs sans relation et de champs ``ForeignKey`` |
|---|
| 2206 |
avec cette méthode, et la valeur doit être une valeur écrite en dur (c-à -d, il est |
|---|
| 2207 |
impossible d'affecter à un champ un autre champ à un moment particulier). |
|---|
| 2208 |
|
|---|
| 2209 |
Pour mettre à jour les champs ``ForeignKey``, on affecte à la nouvelle valeur |
|---|
| 2210 |
le nouveau modÚle vers lequel on désire pointer. Exemple:: |
|---|
| 2211 |
|
|---|
| 2212 |
b = Blog.objects.get(pk=1) |
|---|
| 2213 |
# change chaque Billet afin qu'il appartienne à ce Blog. |
|---|
| 2214 |
Billet.objects.all().update(blog=b) |
|---|
| 2215 |
|
|---|
| 2216 |
La méthode ``update()`` est appliquée instantanément et ne renvoie rien |
|---|
| 2217 |
(tout comme ``delete()``). La seule restriction sur le ``QuerySet`` mis à jour |
|---|
| 2218 |
est qu'il n'accÚde uniquement à une table de la base de données, la table |
|---|
| 2219 |
principale du modÚle. Inutile d'essayer de filtrer sur des champs reliés ou |
|---|
| 2220 |
quelque chose dans le genre, cela ne marchera pas. |
|---|
| 2221 |
|
|---|
| 2222 |
Méthodes d'instance supplémentaires |
|---|
| 2223 |
=================================== |
|---|
| 2224 |
|
|---|
| 2225 |
En plus des méthodes ``save()`` et ``delete()``, un objet modÚle peut avoir |
|---|
| 2226 |
une ou plusieurs des méthodes suivantes: |
|---|
| 2227 |
|
|---|
| 2228 |
get_FOO_display() |
|---|
| 2229 |
----------------- |
|---|
| 2230 |
|
|---|
| 2231 |
Pour chaque champ ayant un paramÚtre ``choices``, l'objet aura une méthode |
|---|
| 2232 |
``get_FOO_display()``, où ``FOO`` est le nom du champ. Cette méthode renvoie |
|---|
| 2233 |
la valeur "lisible" du champ. Par exemple, dans le modÚle suivant:: |
|---|
| 2234 |
|
|---|
| 2235 |
GENRE_CHOIX = ( |
|---|
| 2236 |
('H', 'Homme'), |
|---|
| 2237 |
('F', 'Femme'), |
|---|
| 2238 |
) |
|---|
| 2239 |
class Personne(models.Model): |
|---|
| 2240 |
nom = models.CharField(max_length=20) |
|---|
| 2241 |
genre = models.CharField(max_length=1, choices=GENRE_CHOIX) |
|---|
| 2242 |
|
|---|
| 2243 |
...chaque instance de ``Personne`` aura une méthode ``get_genre_display()``. Exemple:: |
|---|
| 2244 |
|
|---|
| 2245 |
>>> p = Personne(nom='John', genre='H') |
|---|
| 2246 |
>>> p.save() |
|---|
| 2247 |
>>> p.genre |
|---|
| 2248 |
'H' |
|---|
| 2249 |
>>> p.get_genre_display() |
|---|
| 2250 |
'Homme' |
|---|
| 2251 |
|
|---|
| 2252 |
get_next_by_FOO(\**kwargs) and get_previous_by_FOO(\**kwargs) |
|---|
| 2253 |
------------------------------------------------------------- |
|---|
| 2254 |
|
|---|
| 2255 |
Pour chaque champ ``DateField`` et ``DateTimeField`` n'ayant pas ``null=True``, |
|---|
| 2256 |
l'objet aura les méthodes ``get_next_by_FOO()`` et ``get_previous_by_FOO()``, |
|---|
| 2257 |
où ``FOO`` est le nom du champ. Celles-ci renvoient l'objet suivant et précédent |
|---|
| 2258 |
en accord avec le champ de date, levant l'exception appropriée ``DoesNotExist`` |
|---|
| 2259 |
si nécessaire. |
|---|
| 2260 |
|
|---|
| 2261 |
Ces deux méthodes acceptent des arguments mot-clef facultatifs, qui doivent |
|---|
| 2262 |
être dans le format décrit dans `Recherche sur un champ`_ ci-dessus. |
|---|
| 2263 |
|
|---|
| 2264 |
Notez qu'en cas de valeurs identiques, ces méthodes utiliseront l'ID en dernier |
|---|
| 2265 |
recours. Cela garantie qu'aucun enregistrement ne sera sauté ou dupliqué. |
|---|
| 2266 |
Pour un exemple complet, voir l'exemple d'`utilisation de l'API de recherche sur les modÚles`_. |
|---|
| 2267 |
|
|---|
| 2268 |
.. _utilisation de l'API de recherche sur les modÚles: ../models/lookup/ |
|---|
| 2269 |
|
|---|
| 2270 |
get_FOO_filename() |
|---|
| 2271 |
------------------ |
|---|
| 2272 |
|
|---|
| 2273 |
Pour chaque champ ``FileField``, l'objet aura une méthode ``get_FOO_filename()``, |
|---|
| 2274 |
où ``FOO`` est le nom du champ. Cela renvoie le chemin absolu du systÚme de fichier |
|---|
| 2275 |
vers le fichier, selon le paramÚtre ``MEDIA_ROOT``. |
|---|
| 2276 |
|
|---|
| 2277 |
Notez que la classe ``ImageField`` est une sous-classe de ``FileField``, et par |
|---|
| 2278 |
conséquent, chaque modÚle avec un champ ``ImageField`` possédera cette méthode. |
|---|
| 2279 |
|
|---|
| 2280 |
get_FOO_url() |
|---|
| 2281 |
------------- |
|---|
| 2282 |
|
|---|
| 2283 |
Pour chaque champ ``FileField``, l'objet aura une méthode ``get_FOO_url()``, |
|---|
| 2284 |
où ``FOO`` est le nom du champ. Cela renvoie le chemin URL complet vers le fichier, |
|---|
| 2285 |
selon le paramÚtre ``MEDIA_URL``. Si la valeur est nulle, cette méthode renvoie |
|---|
| 2286 |
une chaîne de caractÚres vide. |
|---|
| 2287 |
|
|---|
| 2288 |
get_FOO_size() |
|---|
| 2289 |
-------------- |
|---|
| 2290 |
|
|---|
| 2291 |
Pour chaque champ ``FileField``, l'objet aura une méthode ``get_FOO_size()``, |
|---|
| 2292 |
où ``FOO`` est le nom du champ. Cela renvoie la taille du fichier, en octets. |
|---|
| 2293 |
(En coulisse, cela utilise `os.path.getsize``.) |
|---|
| 2294 |
|
|---|
| 2295 |
save_FOO_file(nom_fichier, contenus_bruts) |
|---|
| 2296 |
------------------------------------------ |
|---|
| 2297 |
|
|---|
| 2298 |
Pour chaque champ ``FileField``, l'objet aura une méthode ``save_FOO_file()``, |
|---|
| 2299 |
où ``FOO`` est le nom du champ. Cela enregistre le fichier dans le systÚme |
|---|
| 2300 |
de fichier, en utilisant le nom passé en argument. Si un fichier avec le nom |
|---|
| 2301 |
donné existe déjà , Django ajoute un caractÚre de soulignement à la fin du |
|---|
| 2302 |
nom (mais avant l'extension) jusqu'Ã ce que le nom de fichier soit disponible. |
|---|
| 2303 |
|
|---|
| 2304 |
get_FOO_height() and get_FOO_width() |
|---|
| 2305 |
------------------------------------ |
|---|
| 2306 |
|
|---|
| 2307 |
Pour chaque champ ``ImageField``, l'objet aura les méthodes ``get_FOO_height()`` |
|---|
| 2308 |
et ``get_FOO_width()``, où ``FOO`` est le nom du champ. Celles-ci renvoient |
|---|
| 2309 |
la hauteur (ou largueur) de l'image, sous la forme d'un entier, en pixels. |
|---|
| 2310 |
|
|---|
| 2311 |
Raccourcis |
|---|
| 2312 |
========== |
|---|
| 2313 |
|
|---|
| 2314 |
Lorsque vous développerez des vues, vous découvrirez un certain nombre |
|---|
| 2315 |
d'idiomes dans la maniÚre dont vous utilisez l'API de la base de données. |
|---|
| 2316 |
Django contient certains de ces idiomes en tant que raccourcis qui peuvent |
|---|
| 2317 |
être utilisés pour simplifier l'écriture des vues. Ces fonctions sont dans |
|---|
| 2318 |
le module ``django.shortcuts``. |
|---|
| 2319 |
|
|---|
| 2320 |
get_object_or_404() |
|---|
| 2321 |
------------------- |
|---|
| 2322 |
|
|---|
| 2323 |
Un idiome commun est d'utiliser ``get()`` et de lever ``Http404`` si l'objet |
|---|
| 2324 |
n'existe pas. Cet idiome est retranscrit à travers ``get_object_or_404()``. |
|---|
| 2325 |
Cette fonction prends un modÚle Django en premier argument et un |
|---|
| 2326 |
nombre arbitraire d'arguments mot-clef, qui seront passés à la fonction |
|---|
| 2327 |
``get()`` du ``Manager``. Cela lÚve ``Http404`` si l'objet n'existe pas. |
|---|
| 2328 |
Par exemple:: |
|---|
| 2329 |
|
|---|
| 2330 |
# récupÚre le Billet avec une clef primaire de 3 |
|---|
| 2331 |
b = get_object_or_404(Billet, pk=3) |
|---|
| 2332 |
|
|---|
| 2333 |
Lorsque l'on fournit un modÚle à cette fonction raccourci, le ``Manager`` par |
|---|
| 2334 |
défaut est utilisé pour exécuter la requête ``get()`` sous-jacente. Si l'on |
|---|
| 2335 |
désire pas utiliser le ``Manager`` par défaut, ou si l'on veut chercher dans une |
|---|
| 2336 |
liste d'objets reliés, on peut fournir à ``get_object_or_404()`` un objet ``Manager`` |
|---|
| 2337 |
à la place. Par exemple:: |
|---|
| 2338 |
|
|---|
| 2339 |
# récupÚre l'auteur du blog e avec un nom de 'Fred' |
|---|
| 2340 |
a = get_object_or_404(b.auteurs, nom='Fred') |
|---|
| 2341 |
|
|---|
| 2342 |
# utilise un manager personnalisé 'billets_recent' pour la |
|---|
| 2343 |
# recherche d'un billet avec la clef primaire de 3 |
|---|
| 2344 |
b = get_object_or_404(Billet.billets_recent, pk=3) |
|---|
| 2345 |
|
|---|
| 2346 |
**Nouveau dans la version de développement:** Le premier argument de ``get_object_or_404()`` |
|---|
| 2347 |
peut aussi être un objet ``QuerySet``. Cela est utile dans les cas où l'on a |
|---|
| 2348 |
définit une méthode de ``Manager`` personnalisée. Par exemple:: |
|---|
| 2349 |
|
|---|
| 2350 |
# utilise un QuerySet renvoyé par la méthode 'deja_publie' |
|---|
| 2351 |
# du Manager personnalisé pour la recherche d'un billet avec |
|---|
| 2352 |
#la clef primaire de 5 |
|---|
| 2353 |
b = get_object_or_404(Billet.objects.deja_publie(), pk=5) |
|---|
| 2354 |
|
|---|
| 2355 |
get_list_or_404() |
|---|
| 2356 |
----------------- |
|---|
| 2357 |
|
|---|
| 2358 |
``get_list_or_404`` se comporte de la même maniÚre que ``get_object_or_404()``, |
|---|
| 2359 |
excepté qu'elle utilise ``filter()`` à la place de ``get()``. Cela lÚve |
|---|
| 2360 |
``Http404`` si la liste est vide. |
|---|
| 2361 |
|
|---|
| 2362 |
Se rabattre sur SQL |
|---|
| 2363 |
=================== |
|---|
| 2364 |
|
|---|
| 2365 |
Lorsque qu'il est nécessaire d'écrire une requête SQL qui est trop complexe pour que |
|---|
| 2366 |
Django puisse s'en occuper, il est possible de se rabattre sur des requêtes SQL brutes. |
|---|
| 2367 |
|
|---|
| 2368 |
La maniÚre conseillée de faire cela est de donner à votre modÚle des méthodes |
|---|
| 2369 |
personnalisées ou de personnaliser les méthodes du ``Manager`` qui exécutera les |
|---|
| 2370 |
requêtes. Bien qu'il n'y ai rien dans Django qui exige que les requêtes doivent |
|---|
| 2371 |
appartenir à la couche des modÚles, cette approche conserve toute la logique métier |
|---|
| 2372 |
en un endroit, ce qui est élégant du point de vue de l'organisation du code. |
|---|
| 2373 |
Pour un mode d'emploi, voir `Exécuter du SQL personnalisé`_. |
|---|
| 2374 |
|
|---|
| 2375 |
Finalement, il est important de noter que la couche base de données de Django |
|---|
| 2376 |
est simplement une interface vers votre base de données. Il est possible |
|---|
| 2377 |
d'accéder à votre base de données via d'autres outils, langage de programmation |
|---|
| 2378 |
ou framework de base de données. Il n'y a rien de spécifique à Django dans votre |
|---|
| 2379 |
base de données. |
|---|
| 2380 |
|
|---|
| 2381 |
.. _Exécuter du SQL personnalisé: ../model-api/#ex-cuter-du-sql-personnalis |
|---|