ManytoMany fieldの使い方講座

今日やること
・manytomany fieldをドキュメント使って学習したのでその時のメモ

今回使うテーブルは以下の通りである。

```python
# learn manytomany field
class Publication(models.Model):
    title = models.CharField(max_length=30)

    class Meta:
        ordering = ['title']

    def __str__(self):
        return self.title

class Article(models.Model):
    headline = models.CharField(max_length=100)
    publications = models.ManyToManyField(Publication)
    class Meta:
        ordering = ['headline']

    def __str__(self):
        return self.headline
```

では、Publicationテーブルのレコードを追加してみましょう。

```
In [1]: from core.models import Publication, Article

In [2]: p1 = Publication(title='The Python Journal')

In [3]: p1.save()

In [4]: p2 = Publication(title='Science News')

In [5]: p2.save()

In [6]: p3 = Publication(title='Science Weekly')

In [7]: p3.save()
```

次にArticleテーブルにレコードを追加していこう。

```
In [8]: a1 = Article(headline='Django lets you build Web apps easily')
```

この時、`a1`を保存する前にPublicationとの関連づけをすることはできない
このようなエラーが出てしまう。なので、`a1`を保存してから関連付をしよう。

```
>>> a1.publications.add(p1)
Traceback (most recent call last):
...
ValueError: "<Article: Django lets you build Web apps easily>" needs to have a value for field "id" before this many-to-many relationship can be used.
```
↓

```
#save it!!
>>> a1.save()
Associate the Article with a Publication:
>>> a1.publications.add(p1)
```

これでArticleテーブルの`a1`に`publications(Publicationテーブル)`を関連付けることができた。

どんどん進めていこう！

```

In [14]: a2 = Article(headline='NASA uses Python')

In [15]: a2.save() 

In [16]: a2.publications.add(p1,p2)

In [17]: a2.publications.add(p3)

In [18]: a2.publications.add(p3)
```

17, 18番目で`p3`を２回追加しているが複数回同じものを関連付をすることも可能である。

次に`create()`を使ってArticleにPublicationを追加していこう。

```
new_publication = a2.publications.create(title='Highlights for Children')
```

これで`a2`に関連付けたpublicationに`title='Highlights for Children'`を追加することができた。
Articleオブジェクトは関連したPublicationオブジェクトにアクセスすることができるのでその確認をしたい。

```
In [21]: a1.publications.all()
Out[21]: <QuerySet [<Publication: The Python Journal>]>
In [22]: a2.publications.all()
Out[22]: <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]>
```
Publicationオブジェクトは関連したArticleオブジェクトにアクセスすることができる。

```
In [23]: p2.article_set.all()
Out[23]: <QuerySet [<Article: NASA uses Python>]>

In [24]: p1.article_set.all()
Out[24]: <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>
```
次にフィルタリングを使って取得したいquerysetを取得しよう。

```
In [21]: 
#Publicationオブジェクトのidを使って取得
Article.objects.filter(publications__id=1)
Out[21]: <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>

In [22]: 
#pk(主キー)を使って取得
Article.objects.filter(publications__pk=1)
Out[22]: <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>

In [23]: 
#２１、２２と同様
Article.objects.filter(publications=1)
Out[23]: <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>

In [24]:
#publicationsのtitle(絡む)が`Science`で始まるものを取得 Article.objects.filter(publications__title__startswith="Science"
    ...: )
Out[24]: <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>, <Article: NASA uses Python>, <Article: NASA uses Python>]>

In [25]: 
#p1と連携しているArticleを取得
Article.objects.filter(publications=p1)
Out[25]: <QuerySet [<Article: Django lets you build Web apps easily>]>

In [26]:
#distinct()・・・重複を除去してデータを取得する
 Article.objects.filter(publications__title__startswith="Science"
    ...: ).distinct()
Out[26]: <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>, <Article: NASA uses Python>]>

In [27]: 
#count()・・・数を数える
Article.objects.filter(publications__title__startswith="Science"
    ...: ).count()
Out[27]: 4
In [35]: 
`NASA`で始まるものを取得
Publication.objects.filter(article__headline__startswith="NASA")
    ...: 
Out[35]: <QuerySet [<Publication: Highlights for Children>, <Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: Science Weekly>, <Publication: The Python Journal>]>

In [36]: Publication.objects.filter(id=1)
Out[36]: <QuerySet [<Publication: The Python Journal>]>

In [37]: Publication.objects.filter(pk=1)
Out[37]: <QuerySet [<Publication: The Python Journal>]>

In [38]: Publication.objects.filter(article__headline__startswith="NASA")
    ...: 
Out[38]: <QuerySet [<Publication: Highlights for Children>, <Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: Science Weekly>, <Publication: The Python Journal>]>

In [39]: Publication.objects.filter(article__id=1)
Out[39]: <QuerySet [<Publication: The Python Journal>]>

In [40]: Publication.objects.filter(article__pk=1)
Out[40]: <QuerySet [<Publication: The Python Journal>]>

In [41]: Publication.objects.filter(article=1)
Out[41]: <QuerySet [<Publication: The Python Journal>]>

In [42]: Publication.objects.filter(article=a1)
Out[42]: <QuerySet [<Publication: Science News>, <Publication: The Python Journal>]>

In [43]: Publication.objects.filter(article__in=[1,2]).distinct()
Out[43]: <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]>

In [44]: Publication.objects.filter(article__in=[a1,a2]).distinct()
Out[44]: <QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]>
In [46]:
#指定した照合パラメタに一致 しない オブジェクトの集合を表現する
Article.objects.exclude(publications=p2)
Out[46]: <QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>, <Article: NASA uses Python>]>
```