- Django の QuerySet 操作を逆引きで調べたい!
このような方に向けて、Django でよく使う操作を中心にまとめました。
各メソッドの説明はジャンルに分けてありますので、以下の目次からお目当てのコンテンツにジャンプしてご利用ください!
基本の取得方法
まずは超基本的なクエリセットの取得方法を解説します。
Django 公式チュートリアルにも頻出なので説明不要かもしれませんが、基本なので念のためです。
全レコードを取得する
全レコードを取得するには、all() メソッドを使います。
余談ですが、objects はマネージャーオブジェクトと呼ばれていて Django コードを解釈して SQL を発行してくれるという役割があります。
queryset = Model.objects.all()
例えば Model テーブルに 10 個のレコードがあれば、10 個分すべての QuerySet オブジェクトが返ってきます。
プライマリーキーを指定して取得
プライマリキーを指定して、特定のレコードを取得することもできます。
model_instance = Model.objects.get(pk=1)
ここでいう get() は Python の辞書に対して使うメソッドとは違って、Django の objects マネージャを通して使われるものであることに注意です。(つまり、第二引数にデフォルト値を取ることはできない)
また、get() でプライマリキーを指定する場合には、クエリセットではなく単一のモデルインスタンスが返ります。
そのため、ループできない(イテラブルではない)ことにも注意が必要です。
レコードを「文字列」で絞り込んで取得
続いて、「文字列」を基準に絞り込んでレコードを取得する方法をご紹介します。
完全一致で絞り込む
filter() メソッドの引数に、「フィールド名 = 絞り込みたい文字列」を渡しましょう。
queryset = Model.objects.filter(name="john")
上記を実行すると、name が John のレコードだけが取得されます。
AND 条件で絞り込んで取得
「 A かつ B 」のような AND 条件で絞り込みたい場合には、フィールドと条件をカンマ区切りで列挙します。
queryset = Model.objects.filter(name="john", band="beatles")
OR 条件で絞り込んで取得
「A または B」のような OR 条件で絞りたい場合には、フィールド名の後に “__in” をつけます。
queryset = Model.objects.filter(name__in=["john","george"])
複数のフィールドにまたがって OR 条件で絞り込んで取得
複数フィールドにまたがって OR 条件を指定するには、Qオブジェクトと | (パイプ)を組み合わせて表現します。
from django.db.models import Q
queryset = Model.objects.filter(Q(name="john") | Q(age=30))
ちなみに OR 以外を表現する方法についても触れておきます。
- AND
- Qオブジェクト同士をカンマで区切る
- objects = Model.objects.filter(Q(name=”john”), Q(age=30))
- NOT
- Qオブジェクトの先頭に – (ハイフン)をつける
- objects = Model.objects.filter(~Q(name=”john”))
パイプやハイフンを使うところは、やや Pandas に近いかもしれません。
前方一致で絞り込む
ある文字列から始まるレコードを取得するには、”startswith” を使います。
# name が "jo" で始まるレコードを取得
queryset = Model.objects.filter(name__startswith="jo")
後方一致で絞り込む
ある文字列で終わるレコードを取得するには、”endswith” を使います。
# name が "non" で終わるレコードを取得
queryset = Model.objects.filter(name__endswith="non")
中間一致で絞り込む
指定した文字列を含むかどうかを判定するのが contains です。
そのため、中間一致というよりは、文字列の存在をチェックするといった方がわかりやすいかもしれません。
# name に "hn" を含むレコードを取得
queryset = Model.objects.filter(name__contains="hn")
もし、大文字・小文字を無視したい場合には icontains を使うことで実現できます。
# 大文字・小文字を無視する
queryset = Model.objects.filter(name__icontains="hn")
レコードを「数字」で絞り込んで取得
数値型フィールドを使って、数字の大小関係で絞り込む方法です。
より大きい
“__gt” は “greater than” の略で、「より大きい」という意味になります。
# 30 より大きい
queryset = Model.objects.filter(age__gt=30)
より小さい
“__lt” は “less than” の略で、「より小さい」という意味になります。
# 30 より小さい
queryset = Model.objects.filter(age__lt=30)
以上
“__gte” は “greater than equal” の略で、「以上」という意味になります。
# 30 以上
queryset = Model.objects.filter(age__gte=30)
以下
“__lte” は “less than equal” の略で、「以下」という意味になります。
# 30 以下
queryset = Model.objects.filter(age__lte=30)
範囲
範囲を指定する場合は range を用います。
# 30 から 50
queryset = Model.objects.filter(age__range=(30, 50))
その他の絞り込み
これまでご紹介してきた以外の絞り込み方法をご紹介します。
最新・最古のレコードを取得する
“created_at” という DateField がある場合には、次のように日時が最も早い・遅いという基準でレコードを取得できます。
# 最新のレコードを取得する
latest_instance = Model.objects.latest("created_at")
# 最古のレコードを取得する
oldest_instance = Model.objects.earliest("created_at")
最初・最後のレコードを取得する
クエリセットオブジェクトの先頭にあるもの、または最後にあるものを取得する場合です。
# 最初のレコードを取得する
first_instance = Model.objects.all().first()
# 最後のレコードを取得する
last_instance = Model.objects.all().last()
並び替え
並び替えの方法を解説します。
昇順でソート
昇順の場合には order_by() メソッドの引数にフィールド名を指定します。
# name を昇順でソート
queryset = Model.objects.all().order_by("name")
降順でソート
降順の場合には、フィールド名の前に – (ハイフン)をつけてください。
# name を降順でソート
queryset = Model.objects.all().order_by("-name")
複数フィールドを基準にソートする
優先順位に従ってフィールド名をカンマ区切りで書いていきます。
queryset = Model.objects.all().order_by("name", "age")
取得形式を指定する
クエリセット以外の形式で取得する方法をご紹介します。
辞書形式で取得する
values() とすることで、辞書形式で取得ができます。
# 辞書で取得
model_dict = Model.objects.values()
特定カラムをリストで取得する
あるカラムのデータをリストで取得したい場合には、values_list() メソッドを使います。
# age 列をリスト型で取得する
age_list = Model.objects.values_list("age", flat=True)
クエリセットを使って計算する
計算をすることもできます。
合計値を求める
特定フィールドで合計値を計算する場合、aggregate() メソッドを使います。
from django.db.models import Sum
total_price = Product.objects.aggregate(Sum("price"))
print(total_price)
# 実行結果
# {"price__sum": 合計値}
以下のようにすると、戻り値の辞書のキーを変えることもできます。
from django.db.models import Sum
total_price = Product.objects.aggregate(total_price=Sum("price"))
print(total_price)
# 実行結果
# {"tota_price": 合計値}
平均値を求める
from django.db.models import Avg
average = Product.objects.aggregate(Avg("price"))
最大値を求める
from django.db.models import Max
max = Product.objects.aggregate(Max("price"))
最小値
from django.db.models import Min
result = Product.objects.aggregate(Min("price"))
SQL クエリ発行回数の節約
Django では SQL クエリを然るべきタイミングで実行する「遅延評価」という方式を採用しています。
そのため、ForeignKey や ManyToMany フィールドで定義したデータを呼び出す際にはここで紹介するメソッドを利用してデータベースからデータを効率的に取得するのがおすすめです。
ForeignKey のクエリ発行回数を減らす
ForeignKey で定義したフィールドの呼び出しには、select_related() を用います。
queryset = Model.objects.select_related("band").all()
ManyToMany のクエリ発行回数を減らす
ManyToMany で定義したフィールドの呼び出しには、prefetch_related() を使います。
queryset = Model.objects.prefetch_related("band").all()
レコードの更新(追加・削除)
レコードを追加する
# レコードを追加 その1
Model.objects.create(name="John Lennon", age=30, part="guitar")
# レコードを追加 その2
model_instance = Model(name="John Lennon", age=30, part="guitar")
model_instance.save()
レコードがあれば取得して、なければ追加する
該当のレコードがある場合にはモデルインスタンスと、False を返します。
一方で、該当のレコードがない場合には作成の上モデルインスタンスを返し、is_created としてTrue を返す事になります。
# レコードに存在しなければ作成
model_instance, is_created = Model.objects.get_or_create(name="John Lennon", age=30, part="guitar")
特定レコードの値を書き換える
# 変更したいレコードを取得する
model_instance = Model.objects.get(pk=1)
# 内容を変更する
model_instance.name = "Ringo Star"
# 変更を反映する
model_instance.save()
特定のレコードの値を削除する
# 変更したいレコードを取得する
model_instance = Model.objects.get(pk=1)
# 削除する
model_instance.delete()
複数のレコードを一度に削除する
複数レコードを一度に削除する場合も delete() メソッドで行います。
# 変更したいレコードを取得する
queryset = Model.objects.filter(band="beatles")
# 削除する
queryset.delete()
レコードを更新する
レコードを更新するには update メソッドを使います。
# 最初のオブジェクトを取得
model_instance = Model.objects.all().first()
# band フィールドを Beatles に更新
model_instance.update(band="Beatles")
複数レコードを更新する
複数レコードを一括で変更することもできます。
# 最初のオブジェクトを取得
queryset = Model.objects.all()
# 全レコードの band フィールドを Beatles に更新
queryset.update(band="Beatles")
選択された行を他のデータベーストランザクションから一時的にロックする
select_for_update() メソッドを用います。
@transaction.atomic
def my_function():
# データベース操作
instance = Model.objects.select_for_update().get(pk=1)
# ここから先、instance に対する操作が制限される
instance.field += 1
instalnce.save()
上記の通りで、select_for_update() が実行された段階でそのインスタンスへのアクセスが制限されるので、他のトランザクションがこのインスタンスに操作を加えようとすると待機状態になります。
なお、待機ではなく即座いにエラーを返したい場合には以下のように nowait=True とします。
@transaction.atomic
def my_function():
# データベース操作
instance = Model.objects.select_for_update(nowait=True).get(pk=1)
# ここから先、instance に対する操作が制限される
instance.field += 1
instalnce.save()
レコードの存在確認
特定のレコードが存在するかを確認する
is_exists には True または False のBool 値が入ります。
# 存在を確認する
is_exists = Model.objects.filter(name="John").exists()
コメント