約1年ぶりの投稿です。去年はdockerでdjangoの環境構築をする記事を書きました。
今年は新卒で入社しまして、djangoでquerysetsの操作はかなり頻繁にすることが多かったので、よくあるDBのテーブルに、あるレコードがあるかないかの存在判定を仕組みを見ながら、忘備録として残します。
オブジェクトがあるかないかを判定したい。
例えば User にid=5を持ったユーザーがいるか、いないか存在判定をします。
検索するときにid=5と指定すればいいのは簡単にわかりますが、ググっていると
- get()
- filter()
- etc ...
を使ったりして検索しています。
User.objects.get(id=5)
User.objects.filter(id=5)
ふむ、どっちやねん。。。となり混乱することがありましたので公式ドキュメント QuerySet API(https://docs.djangoproject.com/ja/3.1/ref/models/querysets/ )を見ていきます。
この書き方によってのちにかく判定方法が変わっていきます。
QuerySetの仕組みを理解する
- モデルマネージャー
- クエリセットAPI
について理解します。
モデルマネージャ
User.objects.all()
のobjectsのことです。modelがクエリ操作をするインターフェースとして持っています。
しかしDBと直接にはやりとりしません。
クエリセットAPI
モデルマネージャーさんはクエリセットAPIを呼び出してDBを操作します。シャイです。
クエリセットAPIは
- DBにアクセスして欲しいオブジェクト
- DBにアクセスしないでクエリセット
を返します。(正確にはapiが提供しているメソッドからも2種類ではない)
そしてクエリセットが帰ってきた場合、DBにアクセスしていないので(遅延評価)、さらにクエリセットAPIをチェーンして書くことができます。
User.objects.filter(id=5).get()
get()とfilter()でどう違うのか
get()はオブジェクトを返します。しかしfilter()はquerysetを返します。
なのでget(id=5)として存在しなかった場合は「オブジェクトが存在しない」という例外を出します。
try:
user = User.objects.get(id=9) # 9のユーザーは最初から存在しない
except User.DoesNotExist: #例外を出す
# 例外が出た時の処理
from django.core.exceptions import ObjectDoesNotExist
try:
user = User.objects.get(id=9)
except ObjectDoesNotExist: #例外を出す
こっちのエラーをimportして書くより、上の書き方をした方がimportしないのでコード量がちょっと減ります。
判定する時の使い分けはどうすればいいのか?
-
get()は1つのオブジェクトしか取らないので、例外処理で有無を判定
-
filter()はクエリセットなので複数のオブジェクトが入っている可能性があり、その数を使って判定に使える。
存在をそのまま調べるときはfilter().exsits()
# Returns True if the QuerySet contains any results, and False if not.
if User.objects.filter(id=5).exists():
# 存在していた時の処理
filter()での判定
複数ある
- len()で数える
- count()を使う
公式ドキュメントにあるようにメモリーをパンクさせないように、一度全部取得しないといけないlen(queryset)よりcount()を推奨。ただし
1)後々そのクエリセットを使う場合
2)あえてmemoryに積ませたい場合
はlen()
- exists()を使う(上のパートで既出)
# len()を使用する場合
user = User.objects.filter(id=5)
if len(user) == 1:
# もしquerysetのなかにid5のuserが1つだけの時の処理
# count()を使用する場合
user = User.objects.filter(id=5).count()
# Returns the number of users whose id contains 5
まとめ
オブジェクトの判定をするのにもいろんな方法があるのですが、公式ドキュメントでquerysetを返すのか何を返すのかページの右のリストにわかりやすくまとまっているので、やはり公式ドキュメントを何度も見ながら作業をすることになるでしょう。
参考: django 公式ドキュメント
https://docs.djangoproject.com/ja/3.1/ref/models/querysets