Django
F()式
サーチ…
前書き
F()式は、DjangoがPythonオブジェクトを使用して、データベースのモデルフィールドや注釈付きの列の値をPythonメモリにプルする必要なく参照する方法です。これにより、開発者は特定のレース条件を回避し、モデルフィールド値に基づいて結果をフィルタリングすることができます。
構文
- django.db.modelsからのインポートF
競合状態の回避
競合状況がわからない場合は、 このQ&Aの質問を参照してください。
次のコードは競合条件の対象となる場合があります。
article = Article.objects.get(pk=69)
article.views_count += 1
article.save()
views_countが1337に等しい場合、このようなクエリが発生します。
UPDATE app_article SET views_count = 1338 WHERE id=69
2つのクライアントが同時にこの記事にアクセスした場合、何が起こることがあり、第2のHTTPリクエストが実行されるということですArticle.objects.get(pk=69)最初に実行する前にarticle.save() 。したがって、両方の要求はviews_count = 1337を持ち、増分してviews_count = 1338をデータベースに保存しますが、実際には1339です。
これを修正するには、 F()式を使用します。
article = Article.objects.get(pk=69)
article.views_count = F('views_count') + 1
article.save()
一方、これはそのようなクエリになります:
UPDATE app_article SET views_count = views_count + 1 WHERE id=69
クエリーセットの一括更新
id 51著者のすべての記事から2つのアップフォォートを削除したいとしましょう。
これをPythonでのみ行うと、 Nクエリが実行されます( Nはクエリセット内のアーティクルの数です)。
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.
もし全ての記事をPythonに引っ張ってループしたり、アップボントを減らしたり、更新された記事をデータベースに保存したりするのではなく、別の方法がありましたか?
F()式を使用すると、1つのクエリでそれを実行できます。
Article.objects.filter(author_id=51).update(upvotes=F('upvotes') - 2)
これは、次のSQLクエリで翻訳できます。
UPDATE app_article SET upvotes = upvotes - 2 WHERE author_id = 51
なぜこれは良いですか?
- Pythonが作業を行う代わりに、データベースに負荷を渡します。この負荷は、そのようなクエリを行うために微調整されています。
- 必要な結果を得るために必要なデータベースクエリの数を効果的に削減します。
フィールド間の算術演算を実行する
F()式を使用して、モデルフィールド間の代数ルックアップ/接続を定義するために、モデルフィールド間の算術演算( + 、 - 、 *など)を実行できます。
モデルを次のようにします。
class MyModel(models.Model): int_1 = models.IntegerField() int_2 = models.IntegerField()ここで、
int_1とint_2フィールドがこの式を満たすMyModelテーブルのすべてのオブジェクトを取得すると仮定しますint_1 + int_2 >= 5。annotate()とfilter()を利用すると、次のようになります。result = MyModel.objects.annotate( diff=F(int_1) + F(int_2) ).filter(diff__gte=5)resultには前述のすべてのオブジェクトが含まれるようになりました。
この例ではIntegerフィールドを使用していますが、このメソッドは算術演算を適用できるすべてのフィールドで機能します。