QuerySet
を返さないQuerySet API
QuerySet API第二回目です。
get(**kwargs)
与えられたパラメータと合致するオブジェクトを1つ取得する。もし1つ以上のオブジェクトがパラメータにヒットしたらMultipleObjectsReturned
エラー が返ってくるので注意。pk
とかユニークな値を使ってしか取得しちゃいかん感じ。またもし1つもヒットするオブジェクトがないとDoenNotExist
エラーが返ってくる。本当にある1つのデータが欲しい時のみ使う。
create(**kwargs)
新しいオブジェクトを作るときに使うメソッド。
entry = Entry.objects.create(title="タイトル", body="本文")
force_inset()
っていうメソッドもあって、これは必ず新しいオブジェクトを作るんだけど、create()
でだいたい作れるから普通はcreate()
で作って問題ないとのこと。たとえば、もしpk
を手動で設定している場合にpk
を設定し忘れたり、pk
が一意じゃないとIntegrityError
が返ってくる。
get_or_create(defaults=None, **kwargs)
キーワード引数の条件でオブジェクトを探して、存在しない場合は新しいオブジェクトを作成する。返り値は(Object, created)
のタプル。created
は新たにオブジェクトが作成された場合はTrue
が返ってきます。本来、get()
と例外処理を使って書くところをこのメソッド1つで書ける場合がある。便利。ただし、このメソッドも複数のオブジェクトが見つかるとMultipleObjectsReturned
エラーが発生するので注意。
update_or_create(defaults=None, **kwargs)
指定されたオブジェクトのデータをアップデートする。もし指定されたオブジェクトが存在しなければ作成する。get_or_create()
と同じく(Object, created)
を返す。辞書型をdefaults
というキーワード引数で渡して、ヒットしたオブジェクトはそのキーワード引数の値にアップデートされる。
obj, created = Person.objects.update_or_create( first_name='John', last_name='Lennon', defaults={'first_name': 'Bob'}, )
keyにフィールド名、valueに実際のデータを代入する。
bulk_create(objs, batch_size=None)
オブジェクトのリストを一度のクエリでDBに保存できる。
>>> Entry.objects.bulk_create([ Entry(headline='This is a test'), Entry(headline='This is only a test'), ])
効率が良くて便利ではあるが、注意点がいくつかある。なにを隠そうこのAPI調べてみようと僕が思ったきっかけは、このメソッドにハマったから。。。。
このモデルは
save()
を呼び出さない。pre_save
とpost_save
というシグナルも送らない。もしpkが
AutoField
に設定されてるならpkをセットしない、またpkの取得もできない。
ここを知らずに僕はbulk_create()
したあとにpkを取得しようとして、「あれ取得できない、なんでやろ」と困り果ててました。またM2Mの関係だと動作しないという注意点もあります。ほかにも数点注意すべきことがあるようです。
キーワード引数のbatch_size
は一度のクエリで何個までオブジェクトを生成するかを指定できます。
count()
オブジェクト数をinteger
で返します。実際はSELECT COUNT(*)
というSQL文が実行されていて、処理の重さが違うのですべてのオブジェクトを取得してlen()
するのはよしましょう。
in_bulk(id_list=None, field_name='pk')
リストにフィールドの値を代入して、キーワード引数filed_name
でフィールドを指定すると、与えたリストの値に該当するオブジェクトを返します。
Entry.objects.in_bulk([1]) {1: <Entyr: Hoge>} #返り値は辞書型
iterator(chunk_size=2000)
QuerySetは取得結果をキャッシュしているので、同じ処理を繰り返しても追加のクエリ発行されません。一方で、iterator()
はDBからキャッシュを行わず一つ一つのオブジェクトをDBから直接取ってきます。
latest(*fields)
最新のオブジェクトを取得します。複数のフィールドを指定することで条件をより高度なものにできます。
Entry.objects.latest('published_at', 'updated_at') # 最新の日付が複数あったとき、更新が新しい方を取得する
またMeta
でget_latest_by
を設定することで引数を設定しなくてもMeta
で設定した条件で取得できる。
earliest(*fields)
latest()
とは逆に最古のデータを取得できる。
first()
クエリセットの一番最初のオブジェクトを取得する。オブジェクトが存在しないばあいはNone
を返す。クエリセットに[0]
というインデックスを付与して最初のオブジェクトを取得しようとするとオブジェクトが無い場合に備えて例外処理を追加しなければいけないが、first()
なら存在しないばあいはNone
を返すので処理が簡潔に済む。
last()
first()
の逆で一番最後のオブジェクトを取得する。
aggregate(*args, **kwargs)
QuerySetのデータを集計したものを辞書型で返す。
from django.db.models import Count q = Blog.objects.aggregate(Count('entry')) {'entry__count': 16}
exists()
指定したオブジェクトが存在する場合はTrue
、存在しない場合False
を返す。
update(**kwargs)
指定されたクエリセットの指定したフィールドを更新する。
Entry.objects.filter(title='hoge').update(title='fuga')
delete()
指定されたクエリセットを削除。
Entry.objects.filter(author="kai").delete() # kaiの投稿をすべて削除
explain(format=None, **options)
Django2.1で追加された新しめのメソッド。QuerySetの実行プランを文字列で返します。手元のDjangoが2.1未満なので実際に実行できず。。。ドキュメントから実行例をひろってきました。
print(Blog.objects.filter(title='My Blog').explain()) Seq Scan on blog (cost=0.00..35.50 rows=10 width=12) Filter: (title = 'My Blog'::bpchar)
キーワード引数のformat
はTEXT, JSON, YAML, XMLのどれか1つを受けとり指定されたフォーマットで出力します。