Я не знаю, почему я не могу понять это, я думаю, что это должно быть довольно просто. У меня есть две модели (см. ниже). Я пытаюсь придумать именованный объем для SupplierCategory, который нашел бы весь SupplierCategory (s) (включая: поставщики), кто связал Поставщика (поставщиков), не пусты.
Я попробовал прямое, присоединяются, named_scope :with_suppliers, :joins => :suppliers
который дает мне только категории с поставщиками, но это дает мне каждую категорию, перечисленную отдельно, поэтому если категория имеет 2 поставщиков, я получаю категорию дважды в возвращенном массиве:
В настоящее время я использую:
named_scope :with_suppliers, :include => :suppliers
и затем по моему мнению, я использую:
<%= render :partial => 'category', :collection => @categories.find_all{|c| !c.suppliers.empty? } %>
Не точно красноречивый, но иллюстрирует то, чего я пытаюсь достигнуть.
Определения классов
class SupplierCategory < AR
has_many :suppliers, :order => "name"
end
class Supplier < AR
belongs_to :supplier
end
Вот еще один подход:
named_scope :with_suppliers, :include => :suppliers,
:conditions => "suppliers.id IS NOT NULL"
Это работает, потому что Rails использует OUTER
JOIN для предложения include
. Если совпадающие строки не найдены, запрос возвращает значения NULL для столбцов поставщика. Следовательно, проверка NOT NULL
возвращает совпадающие строки.
Rails 4
scope :with_suppliers, { includes(:steps).where("steps.id IS NOT NULL") }
Или используя статический метод:
def self.with_suppliers
includes(:steps).where("steps.id IS NOT NULL")
end
Примечание:
Это решение активно загружает поставщиков.
categories = SupplierCategory.with_suppliers
categories.first.suppliers #loaded from memory
class SupplierCategory < AR
has_many :supliers
def self.with_supliers
self.all.reject{ |c| c.supliers.empty? }
end
end
SupplierCategory.with_supliers
#=> Array of SuplierCategories with supliers
Другой способ, более гибкий, используя named_scope
class SupplierCategory < AR
has_many :supliers
named_scope :with_supliers, :joins => :supliers, :select => 'distinct(suplier_categories.id), suplier_categories.*', :having => "count(supliers.id) > 0"
end
SupplierCategory.with_supliers(:all, :limit => 4)
#=> first 4 SupplierCategories with suppliers
Более простая версия:
named_scope :with_suppliers, :joins => :suppliers, :group => :id
Если вы хотите его использовать часто рекомендуется использовать counter_cache .
Я думаю, это будет что-то вроде
#model SupplierCategory
named_scope :with_suppliers,
:joins => :suppliers,
:select => "distinct(supplier_categories), supplier_categories.*",
:conditions => "suppliers.supplier_categories_id = supplier_categories.id"
Дайте мне знать, если это сработает для вас.
Изменить: Используя идею fl00r:
named_scope :with_suppliers,
:joins => :suppliers,
:select => "distinct(supplier_categories), supplier_categories.*",
:having => "count(supliers.id) > 0"
Я считаю, что это более быстрый способ.