2010-02-19 14 views
7

Co według ciebie jest najbardziej optymalnym sposobem pobrania wszystkich atrybutów dla wszystkich skojarzeń, które ma model AR?Odzyskujesz wszystkie atrybuty asocjacji modelu AR?

tj. Załóżmy, że mamy model 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 

Chciałbym odzyskać wszystkie atrybuty Stowarzyszenia poprzez wywołanie metody na przykład docelowy:

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 => { ... }, .... 
    } 

Obecnie dokonać tej pracy przez powtarzanie każdego stowarzyszenia, a następnie na każdym modelu stowarzyszenie, ale to trochę kosztowne. Jak myślisz, jak mogę to zoptymalizować?

Tylko uwaga: zdałem sobie sprawę, że nie można nazwać target.countries_attributes na has_many skojarzeń z nested_attributes, one_to_one stowarzyszenia pozwalają zadzwonić target.country_attributes

Odpowiedz

16

nie jestem jasne, na co masz na myśli z iteracji na wszystkich stowarzyszeń. Czy korzystasz już z refleksji?

Wciąż ciekaw czy istnieje neater sposób, ale to, co mogłam wymyślić, co mniej lub bardziej wyniki w hash jesteś pokazano w przykładzie:

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 

A oto sesja IRB przez script/console, aby pokazać, jak to działa. Po pierwsze, niektóre środowiska:

>> 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>] 

A oto wyjście z nowej metody:

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

Tak, użyłem odbić, ale ta metoda jest bardziej optymalna, Tylko jedna rzecz, usunąłem "wszystko" z 'object_or_array = me.send (association_name) .all' To naprawdę nie jest konieczne i wkręca rzeczy, jeśli są' relacje one_to_one'. Dzięki! – jpemberthy

+0

Dobry połów, podedytowałem przykład. I nie ma problemu. :) –

1

spróbować tego z obsługi wyjątków:

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

Jest to zaktualizowana wersja Stéphan Kochen kod dla szyn 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 
Powiązane problemy