Есть способ связать что-то с ассоциативной таблицей ActiveRecord?

Я получаю segments следующим образом:

@segments = Segment.all

Но, Segment has_many products. Видеть:

models / product.rb:

class Product < ActiveRecord::Base
    belongs_to :segment, dependent: :destroy
end

models / segment.rb:

class Segment < ActiveRecord::Base
    has_many :products
end

Проблема в том, что я просто хочу получить продукты, статус которых равен 1. Я могу создать что-то подобное, используя where в модели Segment, но как я могу добиться этого для products?

Что я уже пробовал

Я нашел решение. Взглянуть:

@segments = Segment.find(:all, include: :products, conditions: {products: {status: 1}})

Это сработало, но я думаю, что код может быть лучше.

Почему я думаю, что код может быть лучше

Хорошо, а зачем мне использовать include: :products, если ассоциация уже существует в моделях? Мы связываем вещи через модель, и я уверен, что этого почти достаточно.

Идеи ?

3
Guilherme Oderdenge 20 Мар 2014 в 18:16

5 ответов

Лучший ответ

Несколько советов, которые могут вам помочь.

Для удобства именования я рассматриваю status==1 как active. Конечно, я понятия не имею, что это значит в вашем конкретном случае.

class Product

  ACTIVE=1

  def self.active
    where(status: ACTIVE)
  end

end

Теперь вы пишете что-то вроде:

segment.products.active

И будут возвращены только активные продукты для данного сегмента.

Найденное вами решение, которое будет извлекать все сегменты с (активными) продуктами, можно было бы записать иначе, как показано ниже:

Segment.includes(:products).where(products: {status: 1})

Теперь, почему так подробно: это фактически переводится в запрос sql, поэтому вы должны быть немного более точным в этом отношении.

1
nathanvda 20 Мар 2014 в 18:33
Спасибо, @nathanvda. На самом деле, все ответы были полезны для меня, но ваш звучит более последовательно. Я думаю, что это более СУХОЙ. И еще: вы открыли мне глаза на разные вещи, которые мы можем делать с моделями. Еще раз спасибо!
 – 
Guilherme Oderdenge
20 Мар 2014 в 19:01

Если вам нужны только те, кто имеет статус 1

class Segment < ActiveRecord::Base
  has_many :products, :conditions => { :status => 1 }
end

В рельсах 3 или

class Segment < ActiveRecord::Base
  has_many :products, -> { where status: 1 }
end

В рельсах 4

Очевидно, можно использовать status: true, если это логическое значение

Затем

@segments = Segment.includes(:products)
1
j-dexx 20 Мар 2014 в 18:42
Я предполагаю, что вы хотите с нетерпением загружать продукты, если вам просто нужны сегменты, у которых есть продукты со статусом один, тогда используйте одно из других решений.
 – 
j-dexx
20 Мар 2014 в 18:49
Segment.joins(:products).where("products.status = 1")

Вы также можете использовать include вместо объединений. Но рельсы преобразуют его в соединение внутри, поскольку вы используете атрибут таблицы продуктов в запросе.

2
usha 20 Мар 2014 в 18:27

Связь has_many: products позволяет использовать include:: products в вашей области. Поэтому сомневаться в своем решении не стоит. Это правильно, и это то же самое, что и решения, представленные в других ответах, но с другим синтаксисом.

1
chumakoff 20 Мар 2014 в 18:44

Это должно сработать - и это совместимо с синтаксисом AREL:

@segments = Segment.joins(:products).where(products: {status: 1})

Совсем другое дело решение с include (или includes, как было бы Rails 3/4), потому что оно генерирует запрос с INNER JOIN, а includes генерирует {{X4 }}. Кроме того, includes обычно используется для активной загрузки связанных записей, а не для запросов с JOIN.

1
Marek Lipka 20 Мар 2014 в 18:45
То же решение другим синтаксисом. Не ответить на вопрос.
 – 
chumakoff
20 Мар 2014 в 18:32
@Chumakoff: технически joins != includes, но концептуально то же самое, но с другим синтаксисом.
 – 
lllllll
20 Мар 2014 в 18:51