ヨージとプログラミング

プログラミング勉強の記録

【Rails】ページネーションを「Pagy」で導入する

Pagyを使う理由

Railsアプリにページネーションを導入する際、長くwill_paginationやkaminariが使われてきており、定番となっているのは言うまでもないですね。
2018年11月に登場し、定番たちの間に割って入ろうとしているのがPagyです。Pagyの利点は次のサイトで熱く語られております。

要約すると、定番よりもメモリ使用量が少なく、使い勝手もいいということです。

導入方法

ここから、あくまで私が試した方法を記します。(よりスマートな方法がある場合は教えて下さい。)

gem 'pagy'

からのbundle installして下さい。 その後、少しだけ設定を書く必要がありますが、こちらのQiitaがわかりやすいです。

そのままではエラーになった

class UsersController < ApplicationController
  include Pagy::Backend
  def index
    user_hash = Record.group(:user_id).maximum(:created_at)
    user_list = user_hash.sort_by{ |k, v| v }.reverse.to_h.keys
    @pagy, @users = pagy(User.find(user_list))
  end

上記コードはNoMethodErrorとなりました。

一見エラーになりそうではないのですが、どうもpagyの引数はActiveRecord::Relationクラスに属している必要があるようで、上記のように配列を引数としてしまうとエラーになります。
ActiveRecord::Relationクラスにするにはallwhereの結果を入れる必要ありです。

引数が配列の場合は変換しよう

# 抜粋して再掲
@pagy, @users = pagy(User.find(user_list))

実は私のケースではuser_listは、user_idが入ったリストなので大層な変換は必要ありませんでした。

# 修正後
@pagy, @users = pagy(User.where(id: user_list))

findwhereに取り替えただけです。 デザインは置いておいてエラーなく実装できました。


しかし、多くの人が引っかかるのはモデルからmapselect等でのメソッドでデータを取得しエラーになるパターンでは無いかと思います。

# User.allではActiveRecord_Relationとなる
irb(main):001:0> User.all.class
=> User::ActiveRecord_Relation

# mapメソッドを使うとArrayクラスになってしまう
irb(main):002:0> User.all.map{|user| user}.class
                    〜
  User Load (2.5ms)  SELECT `users`.* FROM `users`
=> Array

ということです。 なので, Pagyを使う時はmap等は直接使わずにwhere等の中で使って下さい。

#例えばこんな感じ
User.where( id: [配列].map{ |user| user.id } )

(参考にさせて頂いた文献)