2010-02-19 7 views
7

¿Cuál cree que es la forma más óptima de recuperar todos los atributos para todas las asociaciones que tiene un modelo AR?Recuperar todos los atributos de asociación de un modelo AR?

i.e: digamos que tenemos el modelo 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 

me gustaría recuperar todos los atributos de la asociación llamando a un método en una instancia de destino:

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

Actualmente hago este trabajo iterando cada asociación, y después de cada modelo de la asociación, pero es un poco caro, ¿cómo crees que puedo optimizar esto?

Sólo una nota: me di cuenta de que no se puede llamar target.countries_attributes en has_many asociaciones con nested_attributes, one_to_one asociaciones permiten llamar target.country_attributes

Respuesta

16

no estoy claro en lo que quiere decir con la iteración en todas las asociaciones. ¿Ya estás usando reflexiones?

Aún curioso si hay una manera más ordenada, pero esto es lo que podría llegar a, que más o menos resultados en el hash que está mostrando en su ejemplo:

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 

Y aquí es una sesión de IRB a través de script/console para mostrar cómo funciona. En primer lugar, algunos medio ambiente:

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

Y aquí está la salida del nuevo método:

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

Sí, estaba usando reflexiones, pero este método es más óptimo, solo una cosa, eliminé el 'todo' de' object_or_array = me.send (association_name) .all' No es realmente necesario y estropea las cosas si hay ' one_to_one' relaciones. ¡Gracias! – jpemberthy

+0

Buena captura, he editado el ejemplo. Y no hay problema :) –

1

probar esto con el manejo de excepciones: Versión

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

Esto se actualiza de Stéphan Kochen Código de 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 
Cuestiones relacionadas