【问题标题】:How to convert an active record with associations to a nested hash?如何将具有关联的活动记录转换为嵌套哈希?
【发布时间】:2017-03-13 08:12:08
【问题描述】:

所以...

我正在编写一些代码,它将查询具有关联的活动记录并以类似于...的格式返回数据...

[{
    foo: {
        id: ...,
        foo_property: ... 
    },
    bar: {
        id: ...,
        bar_property: ...
    }
}, ...]

...顶级活动记录及其关联都有时间戳和其他我想排除的字段。我知道attributesas_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?)

...谢谢!

【问题讨论】:

  • hmmm 你能给我们看看模型吗?

标签: ruby-on-rails ruby activerecord rails-activerecord


【解决方案1】:

我建议使用支持关联的ActiveModel::Serializers

class WhateverSerializer < ApplicationSerializer
  attributes :id, :other_attribute, :third_attribute

  has_many :foos
  has_many :bars
end

这允许您在最终的 JSON 输出中指定您想要的属性(通过attributes)并让您集成关联(它们将分别使用FooSerializerBarSerializer)。

在对应的控制器中可以直接使用

render json: whatever

并且对象将被序列化程序自动包装。或者,如果您有一个 Whatever 对象数组,您可以执行以下操作:

render json: Whatever.all, each_serializer: WhateverSerializer

【讨论】:

    【解决方案2】:

    您可以使用如下语法实现相同的行为:

    假设像 :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。当您有很多关联时,此语法不清楚。

    【讨论】:

      【解决方案3】:

      你可以试试deep_pluck

      Whatever.deep_pluck(
        foo: [:id, :foo_property, ...],
        bar: [:id, :bar_property, ...],
      )
      

      【讨论】:

        【解决方案4】:

        虽然此问题的其他答案为序列化已知关联提供了简洁的解决方案,但以下是一个综合工具,可序列化任何对象的整个基于关联的依赖图,而无需专门枚举每个所需的关联:

        [这是我为“Inspect object with associations”给出的答案的转贴]

        # 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 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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-04-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-12-10
          • 2020-12-22
          • 2011-11-09
          相关资源
          最近更新 更多