chartckickで円グラフを実装(2つのモデルにまたがるデータ)


概要

家計簿WEBアプリで、月毎やカテゴリ毎に支出を可視化したい!
→グラフを簡単に導入したい!
→chart.jsってのがあるらしい!
→でも、基本的な使い方以外の記事が少ないな…。

ということで、自分なりに試行錯誤した内容を記事にしてみました。

なお、導入や基本機能については以下記事を参照してください。
公式のgithub
導入方法と基本機能

開発環境

Ruby 2.7.4 / Ruby on Rails 6.1.4 / RSpec

Docker / Docker-compode / CircleCI(CI/CD) / Heroku / SendGrid(mailer)

完成のイメージ

以下のモデルのアソシエーションから、

以下のようなグラフを作りたい。
Post(支出)モデル元にカテゴリと月毎にまとめたデータを可視化する。
グラフをタップすると、カテゴリ名と支出の小計が表示される

つまり必要なのは、 Category.name, Post.priceの合計, Category.color あたり。
CategoryとPostの2モデルにまたがっているので、面倒そう…。

作りたいデータの形

いくつかの記事を見ていると、
以下のような1つのモデル内で2カラムを取り出すような形の取り扱いが多い。

<%= pie_chart RugbyWorldCupHostCountry.pluck(:held_at, :total_attendance) %>

2モデルにまたがっているの場合はどうすれば…?
調べてみると、でーたの形式は以下のように
index(今回で言う"category.name")とvalue(今回で言うところの"post.price")配列になっているのだそう。

[[data1_index, data1_value], [data2_index, data2_value], … ]

ここまでわかれば後は簡単なはず!

実装

こうすれば、適宜@monthの値を変えて、前月のグラフなども出せます。

controller/hoge.rb
  def month
    family_category_ids = @relationship.category_ids
    # 今月の1日を取得
    @month = Time.zone.now.beginning_of_month

    # 空の配列を用意
    @pie_chart = []

    # カテゴリ毎にループを回す
    family_category_ids.each do |c_id|
      @pie_chart.push(
        [Category.find_by(id: c_id).name, 
          Post.where(category_id: c_id, payment_at: @month.all_month).sum(:price)]
      )
    end

    # @pie_chartとは別にcolorsの配列も用意
    @pie_chart_colors = Category.where(id: family_category_ids).map(&:color)
  end



以下では少しスタイルをいじっています。

  • legend: {display: false}で、凡例を消す
  • donut: trueで真ん中に穴
views/hoge.html.erb
  <%= pie_chart @pie_chart,legend: {display: false}, donut: true, width: "200px", height: "200px", colors: @pie_chart_colors %>

さいごに

以上です。
質問や、「ほかにもこんな実装方法があるよ!」などコメントいただけると幸いです。
どなたかのためになればうれしいです。