1

我有两个模型,其中 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

4

2 回答 2

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
于 2012-07-03T20:59:09.797 回答
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

于 2020-01-23T08:02:57.390 回答