so ...
我正在编写一些代码,这些代码会查询与关联的活动记录,并以类似于...
的格式返回数据[{
foo: {
id: ...,
foo_property: ...
},
bar: {
id: ...,
bar_property: ...
}
}, ...]
...最高级别的活动记录及其关联都有我想排除的时间戳和其他字段。我知道attributes
和as_json
,但这两个都不遵循关系(而不是用foo_id和bar_id代替foo和bar)。我也知道AssociationReflection
及其动态查找可以与一定复杂性执行我想要的事物的关联的能力,但理想情况下...
我想要一种简单,优雅的方式来查询我的数据,以这种转换和属性过滤,即
Whatever.joins(:foos, :bars)
.eager_load(:foos, :bars)
.? # convert results to an array of hashes with association data (under appropriately named hash key)
.? # filter top level entries and association data to not have timestamps, etc. (select?)
...谢谢!
我建议使用ActiveModel ::支持关联的序列化器:
class WhateverSerializer < ApplicationSerializer
attributes :id, :other_attribute, :third_attribute
has_many :foos
has_many :bars
end
这使您可以在最终的JSON输出(通过attributes
)中指定所需的属性,并让您整合关联(它们将分别使用FooSerializer
和BarSerializer
)。
在相应的控制器中您可以使用
render json: whatever
,该对象将由序列化器自动包装。或者,如果您有Whatever
对象的数组,则可以执行以下操作:
render json: Whatever.all, each_serializer: WhateverSerializer
您可以使用以下语法实现相同的行为:
假设一个嵌套的关联,例如:Baz有很多:Bars和:Bar有很多:Foos
render json: Foo.includes(bar: :baz),
include: {bar: {include: {baz: {only: [:attr_a]}},
only: [:attr_b]}}, only: [:attr_1, :attr_2, :attr_3]
但是,我也建议使用ActiveModel :: serializers。当您有很多关联时,此语法尚不清楚。
您可以尝试deep_pluck
Whatever.deep_pluck(
foo: [:id, :foo_property, ...],
bar: [:id, :bar_property, ...],
)
虽然此问题的另一个答案提供了简洁的解决方案,以序列化已知关联,但下面是一个综合工具,可以序列任何对象的基于关联的依赖图,而无需具体专门列举每个所需的关联:
[这是我给"与关联检查对象"给出的答案的重新发布]
# 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 whatever
这与检查并不完全相同,因为它实际上是将对象序列化为哈希结构,但是它将为您提供类似的信息。
请注意,将可能的关联分开分为两组:单数关联(引用单个目标对象)和复数关联(它们是ActiverEcord Collection -collection -collection -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