首页 > 解决方案 > 返回具有关联的 ActiveRecord 对象的干净方法

问题描述

我想只返回所有没有关联的事物模型对象,有没有更好的方法可以在没有和asscoiation_id的情况下做到这一点?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
    ...
  }

标签: ruby-on-railsactiverecordactivemodel

解决方案


解决方案

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模型有一个 column 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. 默认情况下,渲染函数无论如何都会调用它。当您想要传递选项时,您只需要显式地进行该调用。


推荐阅读