2010-02-19 43 views
7

你認爲什麼是檢索AR模型所有關聯的所有屬性的最佳方法?檢索AR模型的所有關聯屬性?

即:假設我們有型號Target

class Target < ActiveRecord::Base 
    has_many :countries 
    has_many :cities 
    has_many :towns 
    has_many :colleges 
    has_many :tags 

    accepts_nested_attributes_for :countries, :cities, ... 
end 

我想通過調用一個方法上的目標實例,以檢索該協會的所有屬性:

target.associations_attributes 
>> { :countries => { "1" => { :name => "United States", :code => "US", :id => 1 }, 
        "2" => { :name => "Canada", :code => "CA", :id => 2 } }, 
    :cities => { "1" => { :name => "New York", :region_id => 1, :id => 1 } }, 
    :regions => { ... }, 
    :colleges => { ... }, .... 
    } 

目前我做這項工作是通過遍歷每個關聯,然後對每個模型該協會,但它是昂貴的,你認爲我可以優化這個?

剛一說明:我意識到,你不能叫target.countries_attributeshas_many協會與nested_attributesone_to_one關聯允許調用target.country_attributes

回答

16

我不是你的意思迭代上的所有關聯什麼明確的。你已經在使用反射?

仍然好奇,如果有一個更合適的方法,但是這是我能想出的,這或多或少導致哈希你展示在你的榜樣:

class Target < ActiveRecord::Base 
    has_many :tags 

    def associations_attributes 
    # Get a list of symbols of the association names in this class 
    association_names = self.class.reflect_on_all_associations.collect { |r| r.name } 
    # Fetch myself again, but include all associations 
    me = self.class.find self.id, :include => association_names 
    # Collect an array of pairs, which we can use to build the hash we want 
    pairs = association_names.collect do |association_name| 
     # Get the association object(s) 
     object_or_array = me.send(association_name) 
     # Build the single pair for this association 
     if object_or_array.is_a? Array 
     # If this is a has_many or the like, use the same array-of-pairs trick 
     # to build a hash of "id => attributes" 
     association_pairs = object_or_array.collect { |o| [o.id, o.attributes] } 
     [association_name, Hash[*association_pairs.flatten(1)]] 
     else 
     # has_one, belongs_to, etc. 
     [association_name, object_or_array.attributes] 
     end 
    end 
    # Build the final hash 
    Hash[*pairs.flatten(1)] 
    end 
end 

這裏是一個IRB會議通過script/console來顯示它是如何工作的。首先,一些環境:

>> t = Target.create! :name => 'foobar' 
=> #<Target id: 1, name: "foobar"> 
>> t.tags.create! :name => 'blueish' 
=> #<Tag id: 1, name: "blueish", target_id: 1> 
>> t.tags.create! :name => 'friendly' 
=> #<Tag id: 2, name: "friendly", target_id: 1> 
>> t.tags 
=> [#<Tag id: 1, name: "blueish", target_id: 1>, #<Tag id: 2, name: "friendly", target_id: 1>] 

下面是來自新方法的輸出:

>> t.associations_attributes 
=> {:tags=>{1=>{"id"=>1, "name"=>"blueish", "target_id"=>1}, 2=>{"id"=>2, "name"=>"friendly", "target_id"=>1}}} 
+0

是的,我使用的是反射,但是這種方法更加優化,只有一件事,我從'object_or_array = me.send(association_name).all'中刪除了'all'這並不是真的必要,如果有' one_to_one'關係。謝謝! – jpemberthy 2010-02-19 20:43:27

+0

好,我編輯了這個例子。並沒有問題。 :) – 2010-02-19 20:45:13

1

與異常處理試試這個:斯蒂芬Kochen

class Target < ActiveRecord::Base 

    def associations_attributes 
    tmp = {} 
    self.class.reflections.symbolize_keys.keys.each do |key| 
     begin 
     data = self.send(key) || {} 
     if data.is_a?(ActiveRecord::Base) 
      tmp[key] = data.attributes.symbolize_keys! 
     else 
      mapped_data = data.map { |item| item.attributes.symbolize_keys! } 
      tmp[key] = mapped_data.each_with_index.to_h.invert 
     end 
     rescue Exception => e 
     tmp[key] = e.message 
     end 
    end 
    tmp 
    end 

end 
1

這是更新版本代碼爲Rails 4.2

def associations_attributes  
    association_names = self.class.reflect_on_all_associations.collect { |r| r.name } 
    me = self.class.includes(association_names).find self.id  

    pairs = association_names.collect do |association_name|  
     object_or_array = me.send(association_name)  

     if object_or_array.is_a? ActiveRecord::Associations::CollectionProxy 
      association_pairs = object_or_array.collect { |o| [o.id, o.attributes] } 
      [association_name, Hash[*association_pairs.flatten(1)]] 
     else 
      [association_name, object_or_array.attributes]  
     end 
    end  

    Hash[*pairs.flatten(1)] 
end 
相關問題