1
0

More than 1 year has passed since last update.

【DRF】データごとの件数をカウントして返すやり方

Posted at

タイトルの通りなんですけどこれがなかなかできなくて4時間くらいずっとハマってしまったのでメモしておきます。

やりたきこと

タイトルの通りなんですけど、例えば記事モデルと、タグのモデルがあるとします。
そして、タグごとに、「そのタグをつけて投稿してるユーザ数」みたいなデータがほしいとします。

models.py

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

class Article(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    title = models.CharField(max_length=100)
    content = models.TextField()
    tags = models.ManyToManyField(Tag, blank=False)

※実際であれば、Userモデルもあるのですが、上記サンプルコードからは割愛してます。

色々としないといけない気がするんですよね。

  • 逆参照だ!
  • ジョイン的な?
  • SQLでいうgroup by的なこと
  • なんとなくわかるけど、Serializerどうかけばいいんだろう

とかとか。
単純なdjangoなら「こうやればいけるよ」っていうの諸賢方が書いてくださってるのですが、DRFでこういうのが書かれている記事がなかなかなかったんです。Serializerがやはり曲者・・・。

こう書いたらいけた

まず先に、views.pyのほうから説明します。

view.py
class TagRankingListView(generics.ListAPIView):
    queryset = Tag.objects.annotate(Count('article'))
    serializer_class = TagRankingSerializer

queryset = Tag.objects.annotate(Count('article'))

で、そのタグがついたarticleのカウントをannotateして、それをquerysetに入れます。
そして、serializer_classを設定してますね。

そしたらserializer.pyです。

serializer.py
class TagRankingSerializer(serializers.ModelSerializer):

    user_count = serializers.SerializerMethodField()

    class Meta:
        model = Tag
        fields = ('name', 'user_count')

    def get_user_count(self, instance):
        return instance.article__count

user_count = serializers.SerializerMethodField()

で出力する値を一個定義してます。
そして、 class Meta: でモデルと出力するfieldsを定義します。

そして、

    def get_user_count(self, instance):
        return instance.article__count

で、viewsでannotateしたarticle__countを返すような感じで書きます。
※instanceには、Tagが入ります。

これで、 http://127.0.0.1:8000/api/tagranking/ にアクセスすると、

[
    {
        "name": "ビートマニア",
        "user_count": 0
    },
    {
        "name": "オンゲキ",
        "user_count": 0
    },
    {
        "name": "チュウニズム",
        "user_count": 0
    },
    {
        "name": "バイオハザード",
        "user_count": 1
    },
    {
        "name": "プリンセスコネクト Re:Dive",
        "user_count": 0
    },
    {
        "name": "ウマ娘 プリティーダービー",
        "user_count": 3
    },
    {
        "name": "オーバーウォッチ",
        "user_count": 1
    },
    {
        "name": "NIKKE",
        "user_count": 1
    }
]

という値が返ってきます。

serializers.SerializerMethodField()

多分こいつがかなり便利なものでDRFでAPIを書くときに重宝しそうな気がしてます。

これで一応やりたきことは達成できたのですが、
ひとつ難点があって、これめっちゃわかりづらいんですよね。

慣れの問題かもしれませんが、なんかわかりづらい・・・。
viewsとserializerの両方をみてようやくなにを返すAPIかがわかる。みたいな感じなのかな。

きっと、もっとスマートでいい書き方があると思いつつも、それを模索してたら一向に先に進めないのでいったんはこのまま進めるのです。

でも、本当にこれができてすごく感動したんですよ。
なんてったって4時間かかりましたからね。
普通にSQLとかで書けば一瞬なんですけど、いやぁほんとに苦労しました。

最終的に、Kindleで書籍を買ってそれをみてようやく突破した感じです笑。

現場で使える Django REST Framework の教科書[3.2 LTS 対応版] (Django の教科書シリーズ)

普段ならこういう書籍は実物を買うのですが、あまりにも悔しくて今すぐなんとかしたくてこちらをポチりました。
そしたら、Mac版のKindleがまあ相変わらず使いづらいこと・・・笑

でも、これを買って無事解決できたので本当に嬉しかったです。
DRFについての書籍っておそらくこれしかないんじゃないかなって思ったので、時間あるときに一通り目を通しておきたいです。著者の方、本当にありがとうございました。

最後の方はすごく関係ない話ししてしまいましたがそんな感じです。
Django頑張って勉強します。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0