Получить все атрибуты ассоциации модели AR? - PullRequest
8 голосов
/ 19 февраля 2010

Что, по вашему мнению, является наиболее оптимальным способом получения всех атрибутов для всех ассоциаций, которые имеет модель 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:

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_attributes на has_many ассоциации с nested_attributes, one_to_one ассоциации позволяютпозвонить target.country_attributes

Ответы [ 3 ]

16 голосов
/ 19 февраля 2010

Мне не ясно, что вы имеете в виду, когда повторяете все ассоциации.Вы уже используете отражения?

Все еще любопытно, есть ли более аккуратный способ, но я могу придумать, что более или менее приводит к хэшу, который вы показываете в своем примере:

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}}}
1 голос
/ 25 ноября 2017

Это обновленная версия кода Стефана Кочена для 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
1 голос
/ 19 июня 2017

Попробуйте это с обработкой исключений:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...