【问题标题】:How to duplicate a record and its associated records in Ruby on Rails?如何在 Ruby on Rails 中复制记录及其关联记录?
【发布时间】:2013-06-05 10:17:24
【问题描述】:

在我的 Rails 应用程序中,我有 invoices 及其关联的 items

在我的InvoicesController 我添加了这个方法:

def duplicate
  invoice = Invoice.find(params[:id])
  @invoice = invoice.dup
  invoice.items.each do |item|
    @invoice.items << item
  end      
  render :new    
end

def create
  @invoice = current_user.invoices.build(params[:invoice])    
  if @invoice.save
    flash[:success] = "Invoice created."
    redirect_to edit_invoice_path(@invoice)
  else
    render :new
  end
end

单击链接会使用正确的invoiceitems 数据实例化一个表单。

但是,当尝试Create 记录时,我得到一个错误:

Couldn't find Item with ID=4 for Invoice with ID=

谁能告诉我我在这里遗漏了什么,或者是否有更聪明的方法来复制记录包括其相关记录?

感谢您的帮助。

【问题讨论】:

  • 你能发布错误的完整回溯吗?

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


【解决方案1】:

让我们从您的错误消息开始,稍微解构一下。

Couldn't find Item with ID=4 for Invoice with ID=

现在,乍一看,人们可能会认为没有 ID 为 4 的项目。这是一个很好的健全性检查,以确保像这样的简单事情不是问题。在这种情况下,我们有合适的项目,所以我们可以继续。

起初让我感到奇怪的是ID= 后面没有数字。事实证明,这暗示了问题所在。

让我们看看一些控制台输出。我将使用 Cat 对象,仅仅是因为它们很棒。

我们要做的第一件事就是得到一只猫:

Cat.first
=> Cat Load (0.2ms)  SELECT "cats".* FROM "cats" LIMIT 1
=> #<Cat id: 2, age: 6, birthdate: "2013-06-08", color: "brown", name: "Aaron", gender: "m", created_at: "2013-06-08 21:44:22", updated_at: "2013-06-08 21:44:22", user_id: 1> 

我们有了猫之后,让我们复制它。

Cat.first.dup
Cat Load (0.3ms)  SELECT "cats".* FROM "cats" LIMIT 1
=> #<Cat age: 6, birthdate: "2013-06-08", color: "brown", name: "Aaron", gender: "m", created_at: nil, updated_at: nil, user_id: 1> 

关于复制的猫,我们注意到了什么?好吧,对于初学者来说,created_atupdated_at 都是 nil。这通常表明该对象尚未保存到数据库中。如果我们去寻找CatID,我们会注意到甚至没有一个列!

让我们尝试保存新对象。

Cat.first.dup.save
Cat Load (0.3ms)  SELECT "cats".* FROM "cats" ORDER BY "cats"."id" DESC LIMIT 1
(0.1ms)  begin transaction
SQL (0.7ms)  INSERT INTO "cats" ("age", "birthdate", "color", "created_at", "gender", "name", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?, ?, ?, ?)  [["age", 6], ["birthdate", Sat, 08 Jun 2013], ["color", "brown"], ["created_at", Sun, 09 Jun 2013 18:10:47 UTC +00:00], ["gender", "m"], ["name", "Aaron"], ["updated_at", Sun, 09 Jun 2013 18:10:47 UTC +00:00], ["user_id", 1]]
(0.7ms)  commit transaction
=> true 

现在,如果我们调用Cat.last,它将返回这个对象。

Cat.last
Cat Load (0.3ms)  SELECT "cats".* FROM "cats" ORDER BY "cats"."id" DESC LIMIT 1
=> #<Cat id: 11, age: 6, birthdate: "2013-06-08", color: "brown", name: "Aaron", gender: "m", created_at: "2013-06-09 18:10:47", updated_at: "2013-06-09 18:10:47", user_id: 1> 

您的问题是,当您复制 Invoice 时,您并没有将其保存到数据库中。 Couldn't find Item with ID=4 for Invoice with ID= 证实了这一点,因为它告诉我们重复的 Invoice 没有 ID,只有在没有保存的情况下才会出现这种情况。

干杯!

【讨论】:

  • 感谢您的帮助。你的解释对我帮助很大。
【解决方案2】:

这里的 cose 将复制主要对象,以及它下面的每个项目。新对象将不会被保存,项目(目前)也不会。

  def duplicate
    dup.tap do |new_invoice|
      items.each do |item|
        new_invoice.items.push item.dup
      end
    end
  end

还有一个快速测试来证明这些事情:

require 'test_helper'

class InvoiceTest < ActiveSupport::TestCase

  def original_invoice
    Invoice.create(number: 5).tap do |invoice|
      invoice.items.create(name: "a", price: "5")
      invoice.items.create(name: "b", price: "50")
    end
  end
  test "duplication" do
    new_invoice = original_invoice.duplicate
    new_invoice.save
    assert_equal 2, new_invoice.items.count
  end
end

【讨论】:

  • 嘿,杰西。谢谢!作为一个 n00b,你的代码对我来说看起来非常整洁和优雅。我刚刚阅读了tap 方法,但我仍然不明白如何将发票的ID 复制到duplicate 方法中。 (我们肯定需要它,不是吗?)类似于invoice = Invoice.find(params[:id]) 的东西...我认为是tap 方法让我感到困惑...
  • “重复”将在发票模型中...所以您可以像这样使用它:@new_invoice = Invoice.find(params[:id]).duplicate
  • 点击将 1) 归还对象,2) 在归还对象之前先玩弄它
  • 现在我明白了……效果很好!非常感谢您的帮助。
  • 这实际上是我正在寻找的完美Slim controller / Fat model 解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多