以下の内容はhttps://blog.willnet.in/entry/2018/11/28/175136より取得しました。


find_by_sqlしたあとにkaminariでページネーションするには

課題

Railsでページネーション機能を作るときにはkaminari を使うのが定番ですね。Active Recordのクエリメソッドに対してメソッドチェーン形式でpageやperを追加するだけで手軽にページネーションができます。

ただ、find_by_sqlを利用してActive Recordのオブジェクトを作成したときには、戻り値が配列になってしまうのでpageやperなどを後に追加することができません*1。どうしたらよいのでしょうか。

問題のあるやり方

ググるとKaminari.paginate_arrayを使った次のようなやり方がいくつか引っかかります。

Kaminari.paginate_array(array_from_find_by_sql).page(params[:page]).per(10)

Kaminari.paginate_arrayは読んで字のごとく、配列をkaminariで扱えるようにするためのメソッドです。これでとりあえず要件としては満たせますが、レコード件数が多くなってくるとパフォーマンスに問題が出てきます。このやり方だと、find_by_sqlが検索対象のActive Recordオブジェクトを毎回すべて生成する必要があるからです。例えば10000件のレコードが対象となるクエリだとしたら、毎回10000個のActive Recordオブジェクトが生成されるわけです、これはだいぶつらいですね…><。

とりあえずの解決方法

kaminariで生成したモデルオブジェクトは、ビューでpaginateメソッドへの引数になり、ページネーション用のhtmlになります。引数は必ずしもkaminariから作成されたクラスである必要はなく、必要なメソッドが定義されていれば問題ありません。なので必要なメソッドを返すラッパーを用意すればよい、と考えてしばらく↓のようなクラスを作り運用していました(もちろんfind_by_sqlの引数としてlimitやoffsetを使ったクエリを組み立てる必要がありますし、total_countは別のクエリで取得しておく必要があります)。

真の解決方法

しかし、そもそもKaminari.paginate_arrayに次のような形で引数を渡すと、無駄なActive Recordのオブジェクトを生成する必要なく、上記のWrapperForKaminariと同等のメソッドが提供されたオブジェクトが返る、ということに気づきました。

Kaminari.paginate_array(array_from_find_by_sql, limit: 10, offset: 0, total_count: 100)

WrapperForKaminariをgemにしようかな。と思っていたのですが不要でしたね。kaminariべんり。

*1:仮に追加できても、find_by_sqlで発行されるクエリをいじれないので無意味ですね…




以上の内容はhttps://blog.willnet.in/entry/2018/11/28/175136より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14