ransack、フォーム作成時にmatcherが同名になった時の解決方法

0pt   2018-10-11 14:14
IT技術情報局

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: "ほげ")) 試してみたがダメだった deligate と 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を参照するように
deligateを定義。

user.rb delegate :name, to: :profile, prefix: :profile_dot

結論:ransackが直接SQLを発行してしまうため、deligateで定義した profile_dot_name は利用できないのである。

Mysql2::Error: Unknown column 'users.profile_dot_name' in 'where clause': SELECT `users`.* FROM `users` WHERE .....

ちなみに結構ライブラリの中身書き換えてエラー出ないようにしてみたものの最終的にここで詰まって死亡しました

Source: rails tag

   ITアンテナトップページへ
情報処理/ITの話題が沢山。