Django
F () uitdrukkingen
Zoeken…
Invoering
Een F () -uitdrukking is een manier voor Django om een Python-object te gebruiken om te verwijzen naar de waarde van het modelveld of de geannoteerde kolom in de database zonder de waarde naar het Python-geheugen te hoeven halen. Hierdoor kunnen ontwikkelaars bepaalde race-omstandigheden vermijden en ook resultaten filteren op basis van modelveldwaarden.
Syntaxis
- van django.db.models import F
Raceomstandigheden vermijden
Bekijk deze vraag en antwoord als je niet weet wat de raceomstandigheden zijn.
De volgende code kan onderhevig zijn aan racevoorwaarden:
article = Article.objects.get(pk=69)
article.views_count += 1
article.save()
Als views_count
gelijk is aan 1337
, resulteert dit in een dergelijke zoekopdracht:
UPDATE app_article SET views_count = 1338 WHERE id=69
Als twee clients tegelijkertijd toegang hebben tot dit artikel, kan het gebeuren dat het tweede HTTP-verzoek Article.objects.get(pk=69)
uitvoert voordat de eerste article.save()
uitvoert. Beide aanvragen hebben dus views_count = 1337
, verhogen deze en slaan views_count = 1338
op in de database, terwijl dit eigenlijk 1339
zou moeten zijn.
Gebruik een F()
-uitdrukking om dit te verhelpen:
article = Article.objects.get(pk=69)
article.views_count = F('views_count') + 1
article.save()
Dit resulteert daarentegen in een dergelijke zoekopdracht:
UPDATE app_article SET views_count = views_count + 1 WHERE id=69
Queryset in bulk bijwerken
Laten we aannemen dat we 2 upvotes uit alle artikelen van de auteur met id 51
.
Als u dit alleen met Python doet, worden N
query's uitgevoerd ( N
is het aantal artikelen in de queryset):
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.
Wat als er in plaats van alle artikelen in Python te trekken, erover te bladeren, de upvotes te verlagen en elke bijgewerkte versie terug in de database op te slaan, er een andere manier was?
Met een F()
-uitdrukking kan dit in één zoekopdracht:
Article.objects.filter(author_id=51).update(upvotes=F('upvotes') - 2)
Die kan worden vertaald in de volgende SQL-query:
UPDATE app_article SET upvotes = upvotes - 2 WHERE author_id = 51
Waarom is dit beter?
- In plaats van dat Python het werk doet, geven we de belasting door aan de database, die is afgestemd om dergelijke vragen te maken.
- Vermindert effectief het aantal databasequery's dat nodig is om het gewenste resultaat te bereiken.
Rekenkundige bewerkingen tussen velden uitvoeren
F()
-uitdrukkingen kunnen worden gebruikt om rekenkundige bewerkingen ( +
, -
, *
enz.) Tussen modelvelden uit te voeren, om een algebraïsche opzoeking / verbinding daartussen te definiëren.
Laat het model zijn:
class MyModel(models.Model): int_1 = models.IntegerField() int_2 = models.IntegerField()
Laten we nu aannemen dat we alle objecten van de
MyModel
tabel willen ophalenint_2
veldenint_1
enint_2
voldoen aan deze vergelijking:int_1 + int_2 >= 5
. Metannotate()
enfilter()
krijgen we:result = MyModel.objects.annotate( diff=F(int_1) + F(int_2) ).filter(diff__gte=5)
result
bevat nu alle bovengenoemde objecten.
Hoewel het voorbeeld Integer
velden gebruikt, werkt deze methode op elk veld waarop een rekenkundige bewerking kan worden toegepast.