【问题标题】:Clean way to return ActiveRecord object with associations返回具有关联的 ActiveRecord 对象的干净方法
【发布时间】:2020-02-22 00:14:30
【问题描述】:

我想返回所有没有asscoiation_id 的关联的Thing 模型对象,有没有更好的方法可以在没有includeexcept 的情况下做到这一点?

# Thing.rb

belongs_to :object_a
belongs_to :object_b

# create_thing.rb

def change
  create_table :things, id: false do |t|
    t.string :id, limit: 36, primary_key: true
    t.string :object_a_id, foreign_key: true
    t.string :object_b_id, foreign_key: true

    t.timestamps
  end
end
# things_controller.rb

render json: Thing.all, include: [:object_a, :object_b]

output => {
  id: ....
  object_a_id: 'object_a_id',
  object_b_id: 'object_b_id',
  object_a: {
    id: object_a_id
    ...
  },
  object_b: {
    id: object_b_id
    ...
  }

我知道我可以这样做来得到我想要的,但我想知道是否有一种 DRY 方法可以在没有所有包含和除外的情况下做到这一点。

render json: Thing.all, include: [:object_a, :object_b], except: [:object_a_id, :object_b_id]

output => {
  id: ....
  object_a: {
    id: object_a_id
    ...
  },
  object_b: {
    id: object_b_id
    ...
  }

【问题讨论】:

  • 你最后建议的方式非常干燥。你对此有什么感觉?
  • 没有包含会很优雅,除了render json: Thing.all, include: [:object_a, :object_b], except: [:object_a_id, :object_b_id] 之外,因为会有很多这样的端点
  • 一开始我以为我设置的表关系不正确,因为我一直认为可以选择获取具有完整关联对象的模型或仅获取 the association_id

标签: ruby-on-rails activerecord activemodel


【解决方案1】:

解决方案

DRY 方法在您的模型中,您可以定义 attributes 方法并让它返回您希望渲染函数使用的对象的形状。

# thing.rb

def attributes
  # Return the shape of the object
  # You can use symbols if you like instead of string keys
  {
    'id' => id,                      # id of the current thing
    'other_fields' => other_fields,  # add other fields you want in the serialized result   
    'object_a' => object_a,          # explicitly add the associations
    'object_b' => object_b
  }
end

object_aobject_b 的关联应该正常序列化。如果您想限制/自定义它们的序列化结果,可以通过在它们各自的类中添加 attributes 方法来为它们重复相同的方法。

因此,当render json: 在单个事物模型或事物模型集合上调用时,返回的 json 对象的形状将与上述方法中定义的一样。

注意:

需要注意的是,attributes 返回的哈希中的键名必须与方法的名称(或关联名称)匹配。我不太清楚为什么。但是,当需要添加名称与其对应列不同的键时,我使用的解决方法是在我想要使用的键名的模型中创建一个方法。

例如,假设您的 Thing 模型有一个列 name,但在您的 json 结果中,您希望将与该列对应的键名称为 name_of_thing。您将执行以下操作:

def name_of_thing
  name
end

def attributes
  {
    'name_of_thing' => name_of_thing,
    # other fields follow
    # ...
  }
end

条件逻辑

取决于模型中的字段/关联的条件

attributes 方法可以支持模型中基于字段的条件。

# thing.rb

def attributes
  result = {}

  result['id'] = id
  # add other fields

  # For example, if association to object_a exists
  if object_a
    result.merge!({
      'object_a' => object_a
    })
  end

  # return result
  result
end

依赖于模型外部输入的条件

如果你想让你的方法在不同的地方渲染不同的字段,你可以做的一件事是重写as_json方法,因为这个方法接受参数中的选项,所以它可以更好地工作。

# thing.rb

def as_json(options = {})
  result = {}

  result['id'] = id
  # add other fields to result

  if(options[:show_only_ids])
    result.merge!({
      'object_a_id' => object_a_id,
      'object_b_id' => object_b_id
    })
  else
    result.merge!({
      'object_a' => object_a,
      'object_b' => object_b
    })
  end

  # return result
  result
end

然后你需要修改你的控制器(或者你调用Thing模型的序列化的任何地方)以在必要时传递适当的选项。

# thing_controller.rb

render json: Thing.all.as_json({ show_only_ids: true })

渲染时,您不必总是明确指定as_json。默认情况下,渲染函数无论如何都会调用它。当您想要传递选项时,您只需要显式地进行该调用。

【讨论】:

  • 这似乎行得通。我想这又是 Rails 的魔法了。您是否在 Rails 文档中有一个链接,其中讨论了这个或知道如何称呼这个?我将不得不解释这一点
  • 有没有办法控制这种行为?所以我们有一个端点/things,我们只需要association_ids/thing-details,我们需要完整的关联对象。
  • 对于文档,您正在寻找的是 serializable_hash 功能:api.rubyonrails.org/classes/ActiveModel/Serialization.html
  • 为了控制这种行为,我之前在 attributes 方法中添加了一些条件逻辑,没有问题,但这都是由模型本身的数据决定的,而不是控制器。您可能想要采取的方法是在您的模型中覆盖as_json,因为这可以采取选项。然后您可以根据您传递的参数使用条件逻辑。我会更新我的答案,告诉你怎么做。 rubyinrails.com/2019/07/15/override-as-json-rails
  • 没有问题!如果不是太麻烦,你能接受答案吗?谢谢!
猜你喜欢
  • 1970-01-01
  • 2010-10-22
  • 2012-10-14
  • 1970-01-01
  • 2015-10-31
  • 2021-06-21
  • 2018-04-07
相关资源
最近更新 更多