T.H/ 2022年 5月 31日/ 技術

はじめに

こんにちは。T.H.です。
前回、DjangoのManyToManyについてのお話でしたので、それとセットで話題になることの多いannotate,valuesについて記載します。

annotateとは

pythonのannotationと混じりそうな名前ですが、使用目的はだいぶ異なります。
Djangoおけるannotateの目的は大きく二つ、

  • アイテム単位の集計に利用する
  • valuesとセットで使用し、SQLにおけるgroup byのように利用する

なので、ManyToMany(やForeignKey)で定義されたモデルの取得時によく使用します。
早速使ってみましょう。

モデルの用意

まずは以前と同じように多対多の例として、複数のタグをつけられるブログ記事を考えます。
一つのブログ記事には複数のタグをつけることができ、また、一つのタグは複数のブログ記事につけることができます。
DBはMySQL使用を前提とします。

from django.db import models

class Tag(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

class Blog(models.Model):
    title = models.CharField(max_length=100)
    content = models.CharField(max_length=5000)
    tags = models.ManyToManyField(Tag, related_name='tags', blank=True)
    auther_name = models.CharField(max_length=10, blank=False)

    def __str__(self):
        return self.title

アイテム単位の集計

アイテム単位の集計とは、良い言葉が思いつかないのですが、実例を下記に示します。

for tag in Tag.objects.annotate(tag_count=models.Count('tags')):
    print(f'タグ名{tag.name} 使用回数{tag.tag_count}')

とすることで、
Tagごとに紐づいているBlogの数が取得できます。
アクセスする際はannotateの引数左辺に定義した名称で他のカラムと同じように利用できます。

group byのように利用

valuesとセットで使用することで、SQLにおけるgroup byのように利用できます。

for blog in Blog.objects.values('auther_name').annotate(tag_count=models.Count('tags'):
    print(f"著者名{blog.name} タグ数{blog.tag_count}")

とすることで、
著者ごとに何件のTagを使用しているかを取得できます。

この例では非常に単純なモデルであるため、取得後に頑張ってカウントしても大差がないようにみえますが、モデルとフィルタリング条件が複雑になった際にvalues/annotateは役に立つかと思います。
また、実用する際はManyToManyの時に記載したprefetch_relatedも重要になります。

最後に

annotateに関して可能な限りシンプルに記述をしてみました。ここに記述した内容以外にも応用出来ますので、色々と調べてみるとよいかと思います。
DjangoのORマッパーは柔軟で良いですね。

お読みいただきありがとうございました。

About T.H

North Torch株式会社 プログラマ 技術的な経歴は.NETアプリケーションが一番長い。 その他はまだまだ勉強中。