Skip to main content

カウント

Railsのモデルで、モデルとモデルのリレーションが1対多の関係にある時に、親となるモデルが子となるモデルをいくつ保有しているかを把握するのがカウントである。 例えば、ここまでやってきたモジュール開発ではUserモデルは複数のPostモデルを持っている。 具体的にUserモデルのaliceが今いくつpostを作成したかを取得する方法は直接データーベースを叩き、カウントする意外に知る術はない。

問題は、aliceの作成したpostの数を取得するたびにデータベースに問い合わせて計算するのはCPUリソースを膨大に食うことだ。 そこで、Railsはcounter_culture gemを利用する。これは親モデルに子モデルの数を管理するカラムを追加し、postが作成されるたびに+1し削除されたら-1する。 親のUserモデルのカラムに子モデルの個数を保存しておけば、いちいちデータベースで計算しなくても取得できる。

イメージとしてこんな感じだ。

User.find(id: 1).posts.size  # => SELECT COUNT(*)クエリが発生
User.find(id: 1).posts_count # => クエリを発生せずにカウンタキャッシュを使う

インストールとカウント

bundleからcounter_cultureをインストール。

bundle add counter_culture

カウントの機能は上でも解説した通り、モデルが1対多ならばどこでも利用できる。 このモジュールではUser -< Post間のカウントを行ってみる。

UserモデルにPostモデルをカウントするカラムを追加する。

bin/rails g counter_culture User posts_count

マイグレーションを読み込む。

bin/rails db:migrate

ここでコミット

git add .
git commit -m "Install counter_culture and create posts_count"

counter_cultureの設定

1対多のモデルをカウントしたい場合は、子モデルの方に設定を記述する。 今回の場合だとUserが親モデルでPostが子モデルとなる。

app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
counter_culture :user
mount_uploader :thumbnail, BasicUploader
end

設定はこれだけである。

ユーザーの個人情報を表示する画面がまだないので、Railsコンソールと合わせてカウントされているか確認する。

railsコンソールはこのコマンドで入る。

bin/rails c

コンソールでターゲットのユーザーのpostが0な状態を作っておく。

irb(main):008:0> user = User.find(1)
irb(main):008:0> user.posts.destroy_all

userのposts_countを確認する。

irb(main):012:0> user.posts_count
=> 0

ブラウザで同じユーザーにログインして、postを作成する。

new psot

Railsコンソールに移動してuser変数をリロードし更新し、posts_countを表示する。

irb(main):017:0> user.reload
irb(main):018:0> user.posts_count
=> 1

カウントが1つ上がり、この場合user_idが1のユーザーは1つpostを持っていることがわかる。

他にも、多対多の場合のカウントの仕方や変則的なカウント方法など用途に合わせたカウント方法がサポートされている。 詳しくはこちらの記事を参照。

https://techracho.bpsinc.jp/hachi8833/2021_07_01/43698

git add .
git commit -m "Create user and post model counter"