0
1

More than 5 years have passed since last update.

JavaおじさんがPythonを使えるようになるまでの全記録(Djangoのクエリ式としてのF() - 後編)

Posted at

はじめに

そろそろタイトル変えてもいい気がする。
まあ単なる備忘録なのだけど、何書いてるかわかるタイトルにするべきよねー。

というわけでナンバリングやめてみました。
なお、Djangoのクエリ式の前編はこっちね。F()についての説明と排他制御に関して記載してます。

Fの使い方あれこれ

F()の割り当てはモデルの保存後も続く

モデルフィールドに割り当てられたF()オブジェクトはモデルインスタンスの保存後も保持され、各save()に適用されます。 例えば以下のコードを考えます:

reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()

reporter.name = 'Tintin Jr.'
reporter.save()

stories_filed はこの場合2回更新されます。もし初期値が1なら最終的な値は3になります。

...さて、これなんでこんなことになるのかね?
仮説としてはreporter.stories_filed = F('stories_filed') + 1の行でreporter.stories_filedのフィールドに更新用のSQLがセットされるから、ってことになるのかしら。
これはちょっと後で深掘りしてみよう。

フィルター内でF()を使う

F()はQuerySetフィルタでも非常に便利です。
Filterは、Pythonの値ではなくフィールドの値に基づいてオブジェクトのセットをフィルタリングすることを可能にします。
これは、クエリでF()式を使用することで文書化されます。

F() を annotation と一緒に使う

ここ、見出しだけ日本語なんだよなー。

以下のように異なるフィールドを算術演算と組み合わせることで、F()を使用してモデル上に動的フィールドを作成することができます。

company = Company.objects.annotate(
    chairs_needed=F('num_employees') - F('num_chairs'))

(訳注:これはCompanyオブジェクトのそれぞれについてannotateでchairs_neededを導出属性として計算する。具体的には従業員数(num_employees)から椅子の数(num_chairs)を引いたもの)

結合しているフィールドの型が異なる場合は、返されるフィールドの種類をDjangoに伝える必要があります。 F()はoutput_fieldを直接サポートしていないので、ExpressionWrapperで式をラップする必要があります:

from django.db.models import DateTimeField, ExpressionWrapper, F

Ticket.objects.annotate(
    expires=ExpressionWrapper(
        F('active_at') + F('duration'), output_field=DateTimeField()))

(訳注:この例だとactive_atがDateTimeField型でdurationがDuration型と想定しているので、この2つの列の演算を行う場合はExpressionWrapperを使う必要があり、output_fieldが何型になるのかを指定する必要がある)

外部キーなどのリレーショナルフィールドを参照する場合、F()はモデルインスタンスではなくプライマリキー値を返します。

>> car = Company.objects.annotate(built_by=F('manufacturer'))[0]
>> car.manufacturer
<Manufacturer: Toyota>
>> car.built_by
3

(訳注:この例だとCompanyテーブルの最初の行([0])のmanufacturer列に含まれる外部キーをもとにbuilt_byを設定している。そのため、モデルオブジェクトとしてcar.manufacturerは参照先のオブジェクトが表示されるが、導出属性のbuilt_byはpkの値が入ってくる)

null値を含んだソートにF()を使用する

フィールドのヌル値の順序を制御するには、F()とExpression.asc()またはdesc()のnulls_firstまたはnulls_lastキーワード引数を使用します。 デフォルトでは、順序はデータベースによって異なります。
たとえば、連絡を受けた企業の連絡を受けていない企業(last_contactedがnull)を分類するにはこう書きます:

from django.db.models import F
Company.object.order_by(F('last_contacted').desc(nulls_last=True))

(訳注:ここではCompanyテーブルのlast_contacted列がnullである行はソート時に最後に持っていくことを設定している。前回のソースを見るとわかるけどFにascとdescが定義されているのでこんな使い方になる)

というわけでF()単独のトピックはこんなとこかなー。
データベースの関数を使用する場合のFunc()とかGROUP BY句を使うためのAggregate()とかサブクエリを扱うためのSubQueryとかいろいろあるのでそれぞれちゃんとコードとして作らないといかんねー。

まあでも今夜はこの辺で。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1