DjangoのQuerySetのdistinct()の使い方
- 作成日: 2020-12-06
- 更新日: 2023-12-24
- カテゴリ: Django
QuerySetのdistinct()の使い方
PythonのWebフレームワークであるDjango(ジャンゴ)にはQuerySetというオブジェクトがあります。
QuerySetにはfilter()
やorder_by()
などの代表的なメソッドの他に、distinct()というメソッドがあります。
このメソッドを使うとQuerySetの結果から重複したレコード(行、オブジェクト)を除去することが可能です。
- Django documentation | Django documentation | Django
- distinct | QuerySet API reference | Django documentation | Django
この記事ではdistinct()
の使い方を解説します。
具体的には↓を見ていきます。
- モデルを作る
- distinct()を単体で使う
- distinct()とvalues_list()を組み合わせる
- distinct()とorder_by()
モデルを作る
distinct()
の検証用にモデルを作ります。
今回はPerson
と言うモデルを作ります。
これは人物を表現するモデルで、名前や年齢などのフィールドを持っています。
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
age = models.IntegerField()
name
やage
のフィールドにはunique
を付けません。
そのためname
やage
の値は重複する可能性があります。
これらのフィールドの重複したデータをdistinct()
でフィルタリングしてみようという感じです。
↑のPerson
モデルを使ってオブジェクトを↓のように初期化しておきます。
Person.objects.create(name='Taro', age=20)
Person.objects.create(name='Taro', age=30)
Person.objects.create(name='Miki', age=16)
Person.objects.create(name='Miki', age=32)
Person.objects.create(name='Kenji', age=30)
name
においてTaro
, Miki
は重複しています。
またage
は30
の値で重複しています。
distinct()を単体で使う
ではdistinct()
を使ってみたいと思います。
distinct()
は↓のように単体で使うことが出来ます。
Person.objects.distinct()
↑のコードのSQLクエリを見てみましょう。
print(Person.objects.distinct().query)
SELECT DISTINCT "myapp_person"."id", "myapp_person"."name", "myapp_person"."age" FROM "myapp_person"
↑を見るとDISTINCT
のSQL文が生成されているのがわかります。
デフォルトでは↑のようにモデルのすべてのフィールドを条件に指定してクエリを生成しています。
よってPerson.objects.distinct()
の結果は↓のような結果になります。
<QuerySet [
<Person: Person object (1)>,
<Person: Person object (2)>,
<Person: Person object (3)>,
<Person: Person object (4)>,
<Person: Person object (5)>
]>
すべてのオブジェクトが取得されています。
つまりモデルのすべてのフィールドをDISTINCT
の条件にした場合、重複しているレコードはないということになります。
distinct()とvalues_list()を組み合わせる
distinct()
で特定のフィールドを条件に指定したい場合はvalues_list()
と組み合わせます。
distinct()
を呼び出したあとに↓のようにvalues_list()
を呼び出します。
Person.objects.distinct().values_list('name')
↑の場合、values_list()
にはPerson
のフィールドであるname
を指定しています。
こうするとname
のフィールドを条件にDISTINCT
されます。
SQL文を見てみましょう。
print(Person.objects.distinct().values_list('name').query)
SELECT DISTINCT "myapp_person"."name" FROM "myapp_person"
↑のようにSQL文が生成されています。DISTINCT
のフィールドにはname
が単体で指定されています。
よってPerson.objects.distinct().values_list('name')
の結果は↓のようになります。
<QuerySet [('Taro',), ('Miki',), ('Kenji',)]>
name
のフィールドにはTaro
やMiki
が重複でセットされていましたが、結果からそのオブジェクトが取り除かれているのがわかります。
values_list()
には複数の引数(フィールド名)を指定できます。
age
のフィールドも指定してみましょう。
Person.objects.distinct().values_list('name', 'age')
SQL文は↓のようになります。
SELECT DISTINCT "myapp_person"."name", "myapp_person"."age" FROM "myapp_person"
name
とage
がDISTINCT
の条件に指定されました。
Person.objects.distinct().values_list('name', 'age')
の結果は↓のようになります。
<QuerySet [('Taro', 20), ('Taro', 30), ('Miki', 16), ('Miki', 32), ('Kenji', 30)]>
name
とage
を条件にした場合、重複しているオブジェクトは存在しないので↑のようにすべてのオブジェクトが取得されています。
distinct()とorder_by()
distinct()
とorder_by()
を組み合わせる場合は注意が必要です。
order_by()
に指定したフィールドはDISTINCT
に追加されます。
たとえば↓のコードを見てみましょう。
Person.objects.order_by('id').distinct().values_list('name')
order_by()
でid
をキーにソートしてからdistinct()
とvalue_list()
を使っています。
distinct()
とvalue_list()
を組み合わせた場合は、↑の場合はname
だけがDISTINCT
の条件に指定されます。
しかし、↑のようにorder_by()
を指定している場合は違います。
↑のコードのSQL文を見てみましょう。
SELECT DISTINCT "myapp_person"."name", "myapp_person"."id" FROM "myapp_person" ORDER BY "myapp_person"."id" ASC
↑のようにDISTINCT
の条件にid
が設定されています。
このためPerson.objects.order_by('id').distinct().values_list('name')
の結果は↓のようになります。
<QuerySet [('Taro',), ('Taro',), ('Miki',), ('Miki',), ('Kenji',)]>
name
で重複を除去してほしい所でしたが、order_by()
でid
を指定しているので↑のようにid
とname
を条件に重複が除去されます。
結果としてすべてのオブジェクトが取得されます。
おわりに
distinct()
を使うと重複したオブジェクトを結果から除去することが出来ます。
覚えておくと役に立つと思いますので、よろしければこの記事をブックマークしてみてください。