我有两个模型,其中 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
?
我有两个模型,其中 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
?
默认情况下你不能这样做。它可能会在检查对象时产生太多问题和副作用。但是,您可以通过以下方式扩展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
与@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