检索AR模型的所有关联属性?

时间:2010-02-19 19:51:36

标签: ruby-on-rails activerecord

您认为检索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_attributeshas_many nested_attributes关联呼叫one_to_onetarget.country_attributes关联允许致电{{1}}

2 个答案:

答案 0 :(得分: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

这是一个通过script/console的irb会话来展示它的工作原理。首先,一些环境:

>> 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 :(得分:1)

尝试使用异常处理:

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