Rails accept_nested_attributes 创建具有特定 ID 的新资源

Rails accept_nested_attributes creating a new resource with specific id

我收到一个试图创建或更新嵌套资源的传入 SOAP 请求。传入的资源已经有一个 ID,我想将其用作我自己的 ID,而不是生成一个新的 ID。在我继续之前,让我为您提供一些背景信息。

我有以下型号:

class AccountedTime < ApplicationRecord
  belongs_to :article, optional: false
end

class Article < ApplicationRecord
  has_many :accounted_times, dependent: :destroy
  accepts_nested_attributes_for :accounted_times, allow_destroy: true
end

我在创建新资源时遇到的问题,因此假设数据库在每个代码块的开头都是完全空白的。我已经转换了传入数据,使其符合 Rails 格式。

# transformed incoming data
data = {
  id: 1234,
  title: '...',
  body: '...',
  accounted_times_attributes: [{
    id: 12345,
    units: '1.3'.to_d,
    type: 'work',
    billable: true,
  }, {
    id: 12346,
    units: '0.2'.to_d,
    type: 'travel',
    billable: false,
  }],
}

创建非嵌套资源时,您可以提供一个 ID(假设未被占用),它将与提供的 ID 一起保存。

article = Article.find_or_initialize_by(id: data[:id])
article.update(data.except(:accounted_times_attributes))
# The above will save and create a record with id 1234.

但是,当使用更新方法的接受嵌套属性接口时,上述方法不适用于嵌套资源。

article = Article.find_or_initialize_by(id: data[:id])
article.update(data)

ActiveRecord::RecordNotFound: Couldn't find AccountedTime with ID=12345 for Article with ID=1234

我想要达到的结果是使用提供的 ID 创建资源。我想我可能忽略了在提供嵌套属性时切换某种 find_or_initialize_byfind_or_create_by 模式的选项。我在这里遗漏了什么吗?

我目前正在使用以下替代方案(为简单起见没有错误处理):

article = Article.find_or_initialize_by(id: data[:id])
article.update(data.except(:accounted_times_attributes))

data[:accounted_times_attributes]&.each do |attrs|
  accounted_time = article.accounted_times.find_or_initialize_by(id: attrs[:id])
  accounted_time.update(attrs)
end

我觉得你的问题不是accepts_nested_attributes_for配置问题,好像没问题。

也许您的问题是因为您对 Time 模型使用了 reserved word。尝试更改模型的名称并再次测试。

更新:

这看起来很奇怪,但在寻找相同的问题时我发现 this answer 解释了 Rails 3 中错误的可能原因,并显示了两个可能的解决方案。随时查看,希望对您有所帮助。