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
フィールドを使用していますが、このメソッドは算術演算を適用できるすべてのフィールドで機能します。