Ransack, найти запись со всеми связанными записями

У меня есть модель рецепта, в которой есть много ингредиентов, и каждый ингредиент принадлежит предмету. В моей форме расширенного поиска я хочу, чтобы пользователь выбирал несколько ингредиентов, а Ransack мог найти рецепт, содержащий все ингредиенты, выбранные пользователем.

Я попробовал следующее поле поиска:

= f.collection_select(:ingredients_item_id_in, Item.all, :id, :name, {}, {multiple: true})

Но логически это приводит к тому, что отображаются все рецепты, содержащие любой из выбранных ингредиентов.

изменение :ingredients_item_id_in на :ingredients_item_id_in_all приводит к неправильному запросу, поскольку одна запись не может содержать несколько значений item_id.

Любые идеи по созданию этого параметра поиска в Ransack, или мне следует создать для этого подзапрос?

По запросу мой метод контроллера для поиска:

  def search
    @q = Recipe.ransack(params[:q])
    @recipes = @q.result(distinct: true).include_related_models.published
  end

person bo-oz    schedule 17.11.2015    source источник
comment
Не могли бы вы предоставить метод контроллера, который обрабатывает поисковый запрос?   -  person alexanderbird    schedule 24.11.2015
comment
добавил соответствующий код, но это довольно просто..   -  person bo-oz    schedule 24.11.2015
comment
Если вам интересно, include_related_models — это просто именованная область, которая определяет (довольно длинный) include()   -  person bo-oz    schedule 24.11.2015
comment
Вы видели этот вопрос: заголовок stackoverflow.com/questions/13409627/? кажется чем-то похожим, думал, что OP использует другой помощник формы   -  person alexanderbird    schedule 24.11.2015
comment
Нет, я уже много раз искал, но не наткнулся на этот. Кажется, это невозможно тогда (с обыском)... жаль.   -  person bo-oz    schedule 24.11.2015
comment
вы открыты для чистого решения ActiveRecord?   -  person alexanderbird    schedule 24.11.2015
comment
Что ж... Я уже нашел один, но предпочитаю поисковый подход, потому что это лишь малая часть более крупной и расширенной поисковой формы.   -  person bo-oz    schedule 24.11.2015


Ответы (1)


Недавно я столкнулся с похожей задачей для своего проекта (Rails 4.2.4 / Ruby 2.3.1).

Поместье имеет много удобств. Мне нужно получить все поместья, которые включают в себя все выбранные удобства.

Вот как я решил это, используя Ransack

В моем случае у меня есть отношение has_many :through.

недвижимость.rb

class Estate < ActiveRecord::Base
  has_many :estate_comforts
  has_many :comforts, through: :estate_comforts
end

comfort.rb

class Comfort < ActiveRecord::Base
  has_many :estate_comforts
  has_many :estates, through: :estate_comforts
end

estate_comfort.rb

class EstateComfort < ActiveRecord::Base
  belongs_to :estate
  belongs_to :comfort
end

Для сложных запросов вам нужно будет выполнить поиск через post. Для этого вам нужно отредактировать маршруты следующим образом. И добавьте действие search к estates_controlle.rb. Для получения дополнительной информации прочитайте wiki Ransack.

routes.rb

...
resources :estates
  collection do
    match 'search' => 'estates#search', via: %i[get post], as: :search
  end
end

Estates_controller.rb

class EstatesController < ApplicationController
  ...

  def index
    @q = Estate.ransack(params[:q])

    @estates =
      if params[:q]&.has_key?(:estate_comforts_comfort_id_eq_any)
        # Store checked comforts
        session[:estate_comforts_comfort_id_eq_any] = params[:q][:estate_comforts_comfort_id_eq_any]

        comforts_count = params[:q][:estate_comforts_comfort_id_eq_any].count
        ids = @q.result.includes(:estate_comforts).group_by(&:id).select { |_, v| v.count == comforts_count}.keys
        Estate.where(id: ids)
      else
        @q.result(distinct: true)
      end
  end

  def search
    index
    render :index
  end
end

И, наконец, часть шаблона...

поместья/index.haml

= search_form_for @q, url: search_estates_path, html: { method: :post } do |f|
  # here goes the form inputs

  # Polulate checkboxes with previously checked comforts 
  - Comfort.find_each do |comfort|
    # Was checked previously?
    - checked = comfort.id.to_s.in?(session[:estate_comforts_comfort_id_eq_any].to_a)
    %div  
      %input{ name: 'q[estate_comforts_comfort_id_eq_any][]',
              type: "checkbox",
              id: "checkbox#{comfort.id}",
              value: comfort.id,
              checked: checked }
        %label{for: "checkbox#{comfort.id}"}= comfort.name

Будет генерировать следующий html

<form class="estate_search" id="estate_search" action="/estates/search" accept-charset="UTF-8" method="post">
  <div>
    <input checked="" id="checkbox1" name="q[estate_comforts_comfort_id_eq_any][]" type="checkbox" value="1">
    <label for="checkbox1">Comfort Name 1</label>
  </div>
  <div>
    <input id="checkbox2" name="q[estate_comforts_comfort_id_eq_any][]" type="checkbox" value="2">
    <label for="checkbox2">Comfort Name 2</label>
  </div>
</form>

person Pav31    schedule 01.09.2016