开发者

ActiveRecord find all parents that have associated children

开发者 https://www.devze.com 2022-12-28 09:25 出处:网络
I don\'t know why I can\'t figure this out, I think it should be fairly simple.I have two models (see below).I\'m trying to come up with a named scope for SupplierCategory that would find all Supplier

I don't know why I can't figure this out, I think it should be fairly simple. I have two models (see below). I'm trying to come up with a named scope for SupplierCategory that would find all SupplierCategory(s) (including :suppliers) who's associated Supplier(s) are not empty.

I tried a straight up join, named_scope :with_suppliers, :joins => :suppliers which gives me only categories with suppliers, but it gives me each category listed separately, so if a category 开发者_开发知识库has 2 suppliers, i get the category twice in the returned array:

Currently I'm using:

named_scope :with_suppliers, :include => :suppliers

and then in my view I'm using:

<%= render :partial => 'category', :collection => @categories.find_all{|c| !c.suppliers.empty? } %>

Not exactly eloquent but illustrates what I'm trying to achieve.

Class Definitions

class SupplierCategory < AR
  has_many :suppliers, :order => "name"
end

class Supplier < AR
  belongs_to :supplier
end


Here is one more approach:

named_scope :with_suppliers, :include    => :suppliers, 
                             :conditions => "suppliers.id IS NOT NULL"

This works because Rails uses OUTER JOIN for include clause. When no matching rows are found the query returns NULL values for supplier columns. Hence NOT NULL check returns the matching rows.

Rails 4

scope :with_suppliers, { includes(:steps).where("steps.id IS NOT NULL") }

Or using a static method:

def self.with_suppliers
  includes(:steps).where("steps.id IS NOT NULL")
end

Note:

This solution eager loads suppliers.

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

Another way more flexible using 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


Simpler version:

named_scope :with_suppliers, :joins => :suppliers, :group => :id

If you want to use it frequently, consider using counter_cache.


I believe it would be something like

#model SupplierCategory
named_scope :with_suppliers, 
   :joins => :suppliers,
   :select => "distinct(supplier_categories), supplier_categories.*",
   :conditions => "suppliers.supplier_categories_id = supplier_categories.id"

Let me know if it works for you.

Edit: Using fl00r's idea:

named_scope :with_suppliers, 
   :joins => :suppliers,
   :select => "distinct(supplier_categories), supplier_categories.*",
   :having => "count(supliers.id) > 0"

I believe this is the faster way.

0

精彩评论

暂无评论...
验证码 换一张
取 消