用关联检查对象

时间:2012-07-03 20:04:28

标签: ruby-on-rails ruby has-many

我有两个模型,其中A has_many B.如果我加载A包括相关的B:

a = A.find(:first, include: :bs)

a.inspect仅显示a:

的属性
 => "#<A id: 1, name: \"Test\", created_at: \"2012-07-02 21:50:32\", updated_at: \"2012-07-02 21:50:32\">"

如何a.inspect显示所有关联的a.bs

2 个答案:

答案 0 :(得分:1)

默认情况下,您无法执行此操作。检查对象可能会产生太多问题和副作用。但是,您可以使用以下内容扩展inspect

class A < ActiveRecord::Base
  ...
  def inspect
    [super, bs.inspect].join("\n")
  end
end

请注意,这并不是很聪明,因为每次检查bs实例时都会强制加载A。所以也许你想变得更聪明并做这样的事情:

def inspect
  [super, bs.loaded? ? bs.inspect : nil].compact.join("\n")
end

这只会检查bs是否已预先加载(例如:include)。

或许你想创建一个super_inspect而不是自动完成所有事情。您可以使用以下内容扩展ActiveRecord::Base

class ActiveRecord::Base
  def deep_inspect
    ([inspect] + self.class.reflect_on_all_associations.map { |a|
      self.send(a.name).inspect
    }).compact.join("\n  ")
  end
end

这将自动查找与reflect_on_all_associations的所有关联,如果关联已加载,则会在其上调用inspect

现在您可以修改上面的代码,但是您想要创建自己的自定义检查,或者只是根据需要扩展当前检查。用一点代码可以做任何事情。

以下是更智能的更新版本的示例:

class ActiveRecord::Base
  def deep_inspect
    ([inspect] + self.class.reflect_on_all_associations.map { |a|
      out = ""
      assoc = self.send(a.name)
      # Check for collection
      if assoc.is_a?(ActiveRecord::Associations::CollectionProxy)
        # Include name of collection in output
        out += "\n#{assoc.name.pluralize}:\n"
        out += self.send(a.name).to_a.inspect
      else
        out += self.send(a.name).inspect
      end
      out
    }).compact.join("\n  ")
  end
end

答案 1 :(得分:0)

与@Casper的答案相同,这是一个帮助程序方法,用于封存依赖关系链下的所有关联:

# app/models/application_record.rb
#
#   placing the helper in the ApplicationRecord superclass
#   allows all application models to inherit the helper


class ApplicationRecord < ActiveRecord::Base
  def self.marshal

    # collect the names of the objects associations
    single_associations = self.class.reflect_on_all_associations(:has_one ).map {|x| x.name}
    plural_associations = self.class.reflect_on_all_associations(:has_many).map {|x| x.name}

    # serialize the object as a JSON-compatible hash
    self.as_json.merge(

      # merge in a hash containing each `has_one` association via recursive marshalling
      #   the resulting set of associated objects are merged into
      #   the original object's serialized hash, each keyed by the name of the association
        single_associations.reduce({}) { |memo, assoc| memo.merge({ assoc => self.send(assoc).marshal }) }.as_json
    ).merge(

      # merge in the `has_many` associations
      #   the resulting set of associated collections must then be processed
      #   via mapping each collection into an array of singular serialized objects
        plural_associations.reduce({}) { |memo, assoc| memo.merge({ assoc => self.send(assoc).map {|item| item.marshal } }) }.as_json
    )
  end
end

然后您可以通过调用以下方法来调用此帮助方法:

Marshal.serialize a

这与检查并不完全相同,因为它实际上是将对象序列化为哈希结构,但是它将为您提供类似的信息。


请注意,可能的关联被分为两组:单一关联(引用单个目标对象)和多个关联(ActiveRecord CollectionProxy对象,即它们是可枚举的)。因为我们将关联的对象序列化为散列,所以每个has_many关联都必须解析为单个序列化对象的集合(例如,我们将集合内的每个关联映射为其序列化形式)。

belongs_to关联应该被忽略,因为在两个方向上的映射关联都将立即创建一个循环依赖图。如果您想沿“归属链”封送,则可以执行类似的操作

def self.trace
    parent_associations = obj.class.reflect_on_all_associations(:belongs_to).map {|x| x.name}

    obj.as_json.merge single_associations.reduce({}) { |memo, assoc| memo.merge({ assoc => obj.send(assoc).trace }) }.as_json
end