Django
Expressions F ()
Recherche…
Introduction
Une expression F () est un moyen pour Django d'utiliser un objet Python pour faire référence à la valeur du champ modèle ou de la colonne annotée dans la base de données sans avoir à extraire la valeur dans la mémoire Python. Cela permet aux développeurs d'éviter certaines conditions de concurrence et de filtrer les résultats en fonction des valeurs du champ du modèle.
Syntaxe
- à partir de django.db.models import F
Éviter les conditions de course
Voir cette question si vous ne savez pas quelles sont les conditions de course.
Le code suivant peut être soumis à des conditions de course:
article = Article.objects.get(pk=69)
article.views_count += 1
article.save()
Si views_count
est égal à 1337
, cela entraînera une telle requête:
UPDATE app_article SET views_count = 1338 WHERE id=69
Si deux clients accèdent à cet article en même temps, il se peut que la deuxième requête HTTP exécute Article.objects.get(pk=69)
avant que le premier exécute article.save()
. Ainsi, les deux requêtes auront views_count = 1337
, incrémentez-le et enregistrez views_count = 1338
dans la base de données, alors qu'il devrait en fait être 1339
.
Pour résoudre ce problème, utilisez une expression F()
:
article = Article.objects.get(pk=69)
article.views_count = F('views_count') + 1
article.save()
Cela, d'autre part, se traduira par une telle requête:
UPDATE app_article SET views_count = views_count + 1 WHERE id=69
Mise à jour du jeu de requête en vrac
Supposons que nous voulions supprimer 2 relevés supérieurs de tous les articles de l'auteur avec l'ID 51
.
Faire cela uniquement avec Python exécuterait N
requêtes ( N
étant le nombre d'articles dans le jeu de requête):
for article in Article.objects.filter(author_id=51):
article.upvotes -= 2
article.save()
# Note that there is a race condition here but this is not the focus
# of this example.
Que faire si, au lieu d'extraire tous les articles en Python, de les parcourir en boucle, de réduire les relevés et de sauvegarder chaque mise à jour dans la base de données, il y avait un autre moyen?
En utilisant une expression F()
, vous pouvez le faire en une seule requête:
Article.objects.filter(author_id=51).update(upvotes=F('upvotes') - 2)
Qui peut être traduit dans la requête SQL suivante:
UPDATE app_article SET upvotes = upvotes - 2 WHERE author_id = 51
Pourquoi est-ce mieux?
- Au lieu de faire le travail avec Python, nous transmettons la charge à la base de données, qui est adaptée pour effectuer de telles requêtes.
- Réduit efficacement le nombre de requêtes de base de données nécessaires pour obtenir le résultat souhaité.
Exécuter des opérations arithmétiques entre les champs
F()
expressions F()
peuvent être utilisées pour exécuter des opérations arithmétiques ( +
, -
, *
etc.) entre les champs de modèle, afin de définir une recherche / connexion algébrique entre elles.
Laissez le modèle être:
class MyModel(models.Model): int_1 = models.IntegerField() int_2 = models.IntegerField()
Maintenant nous allons supposer que nous voulons récupérer tous les objets de
MyModel
tableau qui a lesint_1
etint_2
champs satisfont à cette équation:int_1 + int_2 >= 5
. En utilisantannotate()
etfilter()
nous obtenons:result = MyModel.objects.annotate( diff=F(int_1) + F(int_2) ).filter(diff__gte=5)
result
contient désormais tous les objets susmentionnés.
Bien que l'exemple utilise des champs Integer
, cette méthode fonctionnera sur tous les champs sur lesquels une opération arithmétique peut être appliquée.