ホーム > カテゴリ > Ruby・Ruby on Rails >

ransackで親子関係テーブルの検索 [Ruby on Rails]

1つのフォームで「親子関係」がある複数のテーブルを検索する方法です。

モデル(テーブル構成)

例として「掲示板システム」で「questions」(質問テーブル)、「answers」(回答テーブル)をransackで扱う仕様とします。

questions - 質問テーブル

項目名備考
idinteger
titlestringタイトル

answers - 回答テーブル

項目名備考
idinteger
question_idintegerquestionsテーブルのid
namestring名前
urlstringURL
bodystring本文

コントローラー側

def index
  @q = Question.all.ransack(params[:q])       
  @questions = @q.result
end

@qは検索パラメータ、@questionsは検索結果です。

ビュー側

SlimとBootstrap4を使用しています。※erbの方は後述するHTMLを参考。

= search_form_for @q, class: 'mb-5' do |f|
  .form-group.row
    = f.label :title_cont, 'タイトル', class: 'col-sm-2 col-form-label'
    .col-sm-10
      = f.search_field :title_cont, class: 'form-control'
  .form-group.row
    = f.label :answers_body_cont, '本文', class: 'col-sm-2 col-form-label'
    .col-sm-10
      = f.search_field :answers_body_cont, class: 'form-control'
  .form-group
    = f.submit class: 'btn btn-outline-primary'

[出力されるHTML]

<form class="mb-5" id="question_search" action="/questions" accept-charset="UTF-8" method="get">
  <div class="form-group row">
    <label class="col-sm-2 col-form-label" for="q_title_cont">タイトル</label>
    <div class="col-sm-10">
      <input class="form-control" type="search" name="q[title_cont]" id="q_title_cont" />
    </div>
  </div>
  <div class="form-group row">
    <label class="col-sm-2 col-form-label" for="q_answers_body_cont">本文</label>
    <div class="col-sm-10">
      <input class="form-control" type="search" name="q[answers_body_cont]" id="q_answers_body_cont" />
    </div>
  </div>
  <div class="form-group">
    <input type="submit" name="commit" value="検索" class="btn btn-outline-primary" data-disable-with="検索" />
  </div>
</form>

モデル側

[question.rb]

class Question < ApplicationRecord
  
  # 複数のAnswerを子に持つ
  has_many :answers
  
  # 検索対象のカラム ※デフォルトは全て
  def self.ransackable_attributes(auth_object = nil)
    %w[title]
  end
end

[answer.rb]

class Answer < ApplicationRecord

  # Questionに属する
  belongs_to :question

  # 検索対象のカラム ※デフォルトは全て
  def self.ransackable_attributes(auth_object = nil)
    %w[body]
  end
end

必ず、「ransackable_attributes」で検索対象のカラム(項目)を指定します。デフォルトだと全てのカラムが対象になり、不正に検索される可能性がありますのでご注意ください。

(おまけ)複数キーワードによるAND検索の実装

タイトル(title)を例にすると次のような変数になります。

通常検索(1つ)title_cont
OR検索(複数)title_cont_any
AND検索(複数)title_cont_all

def index
  # スペース区切りを配列に変換して返す。
  if params[:q].present?
    params[:q]['title_cont_all'] = params[:q]['title_cont_all'].split(/[\p{blank}\s]+/)
  end

  @q = Question.all.ransack(params[:q])       
  @questions = @q.result
end





関連記事



公開日:2019年10月19日 最終更新日:2020年08月26日
記事NO:02795