TL;DR
limit
で絞ったデータを kaminari
で取り扱う場合は
一旦、Arrayに置き換えてから Kaminari.paginate_array
を使って表現する
問題点と回避策
問題点
kaminari
は Modelで検索したデータに
.page
や .per
してあげるだけで
簡単にページネーションできるgemですが
ActiveModel
の limit
は無視されてしまいます。1
例えば『直近で更新したユーザ100人のデータを1ページ5件ずつに表示させたい』
といったようなデータを抜き出すときに
list_data = User.order(updated_at: :desc).limit(100).page(1).per(5)
と書けばできそうな雰囲気がありますが .limit(100)
の内容は無視されてしまうので
Userのデータが100件以上あれば20ページ目以降(100 ÷ 5 = 20)も取れてしまう
list_data = User.order(updated_at: :desc).limit(100).page(25).per(5)
SELECT `users`.* FROM `users` ORDER BY `users`.`updated_at` DESC LIMIT 5 OFFSET 120
# kaminari側でlimitを設定しているので .limit(100) は無視される
回避策
ページネーションするときにDBの検索がかかるため
先にデータの抽出が終わった状態のものに
ページネーション処理を行えば別の処理として扱えます。
なので、一旦Arrayにしてから
そのArrayオブジェクトに対してページネーション処理をすれば
求めていた結果を得ることができます。
先程の例に対して適応すると以下のようになります。
user_result = User.order(updated_at: :desc).limit(100)
user_array = user_result.each_with_object [] do |user, result|
result << user
end
list_data = Kaminari.paginate_array(user_array).page(1).per(5)
ただし、これは部分的に抽出してくれるkaminariの利点を削っているので
大量のデータを絞り込んで使う場合はいらぬ負荷をかけてしまう懸念があるので
ご注意ください。
参考ページ
-
Githubにある、kaminariのReadmeに『Keep in mind that per internally utilizes
limit
and so it will override any limit that was set previously. 』 とある ↩