2018/07/28
Python Django クエリセット ManyToManyManyToMany フィルターなどのオブジェクト操作一覧
モデル同士を紐づける方法は、ForeinKey(1対多)やOneToOne(1対1)などがありますが、今日はManyToMany(多対多)についてです。
ManyToMany(多対多)を作る
PersonモデルとHobbyモデルの関係性で考えてみます。
Personは、サッカーやピアノなど複数のHobbyを持つことが考えられますし、逆に、サッカーというHobbyはたくさんの人の趣味となり得ます。
データベースのイメージは以下のようになります。
このテーブルを作成するモデルは以下の通りです。blank=Trueを指定することで、Personは必ずしもHobbyを持たなくてもよくなります。上の画像の例でいうと「ジョン」のような感じです。
models.py
from django.db import models
class Hobby(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Person(models.Model):
name = models.CharField(max_length=100)
hobbys = models.ManyToManyField(Hobby, blank=True)
def __str__(self):
return self.name
オブジェクト操作
画像のようにデータが保存されていると仮定して、データの扱い方をいくつか紹介します。
参照と逆参照
HobbyとPersonはManyToManyFieldによって紐付けられています。このとき、Person側からみたHobbyとの関係を参照、Hobby側からみたPersonとの関係を逆参照といいます。
それぞれ参照しあっているので、以下のようにデータを取得することができます。逆参照の場合、オブジェクト.クラス名_set
とすることで、クエリセットを取得します。
参照と逆参照
#参照
>>> person1 = Person.objects.get(id=1)
>>> person1.hobbys.all()
<QuerySet [<Hobby: サッカー>, <Hobby: ピアノ>, <Hobby: プログラミング>]>
#逆参照
>>> hobby1 = Hobby.objects.get(id=1)
>>> hobby1.person_set.all()
<QuerySet [<Person: ジョブス>, <Person: ローラ>]>
>>>
作成、追加、取り除き、全削除
ManyToManyフィールドにオブジェクトを追加したり、すでに保管されているオブジェクトを削除したりする方法です。
1つ目のcreateメソッドは、「映画鑑賞」というこれまでになかった新しいHobbyオブジェクトを作成した上で、それをperson1のhobbysフィールドに保管しています。
追加・取り除き・全削除
#作成と保管
>>> person1.hobbys.create(name="映画鑑賞")
>>> person1.hobbys.all()
<QuerySet [<Hobby: サッカー>, <Hobby: ピアノ>, <Hobby: プログラミング>, <Hobby: 映画鑑賞>]>
>>> hobby3 = Hobby.objects.get(id=3)
#追加
>>> person1.hobbys.add(hobby3)
>>> person1.hobbys.all()
<QuerySet [<Hobby: サッカー>, <Hobby: ピアノ>, <Hobby: 読書>, <Hobby: プログラミング>, <Hobby: 映画鑑賞>]>
#取り除き
>>> person1.hobbys.remove(hobby3)
>>> person1.hobbys.all()
<QuerySet [<Hobby: サッカー>, <Hobby: ピアノ>, <Hobby: プログラミング>, <Hobby: 映画鑑賞>]>
#全削除
>>> person1.hobbys.clear()
>>> person1.hobbys.all()
<QuerySet []>
>>>
ManyToManyFieldでフィルターをかける
フィルター
#idが1のHobby(サッカー)を趣味に持つユーザー一覧
>>> Person.objects.filter(hobbys=1)
<QuerySet [<Person: ローラ>, <Person: ジョブス>]>
#nameが"サッカー"のHobbyを趣味に持つユーザー一覧
>>> Person.objects.filter(hobbys__name="サッカー")
<QuerySet [<Person: ローラ>, <Person: ジョブス>]>
#idが1または2のHobbyを持つユーザー一覧
>>> Person.objects.filter(hobbys__in=[1,2])
<QuerySet [<Person: ローラ>, <Person: ジョブス>, <Person: ケイ>, <Person: ジョブス>]>
ManyToManyフィールドのオブジェクトが多い順に表示
ManyToManyフィールドに保存されているオブジェクトが多い順、つまり、持つ趣味が多い順にPersonオブジェクトを並べます。
保有するオブジェクト順
>>> from django.db.models import Count
>>> Person.objects.all().annotate(Count("hobbys")).order_by('-hobbys__count')
<QuerySet [<Person: ジョブス>, <Person: ローラ>, <Person: ケイ>, <Person: ジョン>]
annotateについては、こちらの記事でも紹介しています。