ransackでフォームフィールド名が同じになってしまう問題
まず起きることはないが、どんな状況で起き得るか
Userモデル
name: String # 本名
profile_name: String # サイト上で表示する名前
......
Profileモデル
name: String # いろいろな経緯があって生まれてしまったモデルの名前
......
この状況でransackのフォームを作成すると問題が起きる
- どちらも
profile_name_cont
になってしまうのだ。
ransack_form.slim.erb
= f.label :profile_name_cont, 'サイト上の表示名(user.profile_name)'
= f.check_box(:profile_name_cont)
= f.label :profile_name_cont, 'Profileの名前(user.profile.name)'
= f.check_box(:profile_name_cont)
この場合 同じクラスのメソッドが優先されるので検索されるのは
user.profile_name
だけであり、user.profile.name
の検索は行われない。
結論:処理としては User
モデルの profile_name
カラムの検索だけちゃんと動作する。
2通りの解決方法
- 設計ミスなのでカラム名を変更する、おそらくこれがもっとも良い解決方法。
-
user.profile.name
は ransack を使わずに検索を実装、検索結果を結合する。
検索結果を結合する場合の実装方法
user.profile.name
の検索は ransack
に載せることができないので
別で検索処理を作成して `` 結果を結合 merge
する戦法をとった
検索結果のマージ
基本的な部分は省略するが、ransackと通常の検索結果の merge
はこんな感じでできる。
merge
result = User.ransack(ransack_params).result.order(id: :desc)
result.joins(:profile).merge(Profile.where(name: "ほげ"))
試してみたがダメだった delegate
と ransackable_attributes
を使った実装
ransackable_attributes
で検索可能な要素一覧を表示できる
User.ransackable_attributes
これ毎に Search Matchers
が利用可能なので、ここに profile_dot_name
を追加
そのキーワードでも検索できるようにしてみた。
def self.ransackable_attributes(auth_object = nil)
%w(name profile_name profile_dot_name)
end
profile_dot_name
で検索した際に Profile
モデルの name
を参照するように
delegate
を定義。
user.rb
delegate :name, to: :profile, prefix: :profile_dot
結論:ransackが直接SQLを発行してしまうため、delegate
で定義した profile_dot_name
は利用できないのである。
例
Mysql2::Error: Unknown column 'users.profile_dot_name' in 'where clause': SELECT `users`.* FROM `users` WHERE
.....
ちなみに結構ライブラリの中身書き換えてエラー出ないようにしてみたものの最終的にここで詰まって死亡しました