我想做一个ActiveRecord对象的副本,改变进程中的一个字段(除了id)。要做到这一点,最简单的方法是什么?

我意识到我可以创建一个新记录,然后遍历每个字段,逐字段复制数据—但我认为一定有更简单的方法来做到这一点。

也许是这样的:

 new_record = Record.copy(:id)

当前回答

如果你需要一个有关联的深度拷贝,我推荐deep_cloneable gem。

其他回答

下面是一个覆盖ActiveRecord #dup方法的示例,以自定义实例复制并包括关系复制:

class Offer < ApplicationRecord
  has_many :offer_items

  def dup
    super.tap do |new_offer|

      # change title of the new instance
      new_offer.title = "Copy of #{@offer.title}"

      # duplicate offer_items as well
      self.offer_items.each { |offer_item| new_offer.offer_items << offer_item.dup }
    end
  end
end

注意:这个方法不需要任何外部宝石,但它需要更新的ActiveRecord版本,实现了#dup方法

您还可以检查acts_as_inheritable gem。

Acts As Inheritable是一个Ruby Gem,专门为Rails/ActiveRecord模型编写。它意味着与自引用关联一起使用,或者与具有共享可继承属性的父级的模型一起使用。这将允许您从父模型继承任何属性或关系。”

通过将acts_as_inheritable添加到您的模型中,您将可以访问这些方法:

inherit_attributes

class Person < ActiveRecord::Base

  acts_as_inheritable attributes: %w(favorite_color last_name soccer_team)

  # Associations
  belongs_to  :parent, class_name: 'Person'
  has_many    :children, class_name: 'Person', foreign_key: :parent_id
end

parent = Person.create(last_name: 'Arango', soccer_team: 'Verdolaga', favorite_color:'Green')

son = Person.create(parent: parent)
son.inherit_attributes
son.last_name # => Arango
son.soccer_team # => Verdolaga
son.favorite_color # => Green

inherit_relations

class Person < ActiveRecord::Base

  acts_as_inheritable associations: %w(pet)

  # Associations
  has_one     :pet
end

parent = Person.create(last_name: 'Arango')
parent_pet = Pet.create(person: parent, name: 'Mango', breed:'Golden Retriver')
parent_pet.inspect #=> #<Pet id: 1, person_id: 1, name: "Mango", breed: "Golden Retriver">

son = Person.create(parent: parent)
son.inherit_relations
son.pet.inspect # => #<Pet id: 2, person_id: 2, name: "Mango", breed: "Golden Retriver">

希望这能帮助到你。

根据您的需要和编程风格,还可以结合使用类的新方法和merge。由于缺乏更好的简单示例,假设您有一个计划在某个日期执行的任务,并且希望将其复制到另一个日期。任务的实际属性并不重要,所以:

old_task = Task.find(task_id)
new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date}))

将创建一个新任务:id => nil,:scheduled_on => some_new_date,所有其他属性与原始任务相同。使用任务。new,你必须显式调用save,所以如果你想自动保存它,改变任务。new to Task.create。

和平。

你可能也喜欢ActiveRecord 3.2的Amoeba宝石。

在您的情况下,您可能希望使用配置DSL中可用的nullify、regex或前缀选项。

它支持has_one, has_many和has_and_belongs_to_many关联的简单和自动递归复制,字段预处理和高度灵活和强大的配置DSL,可以应用于模型和动态。

一定要查看阿米巴文档,但是使用起来很简单…

just

gem install amoeba

或添加

gem 'amoeba'

到您的Gemfile

然后将阿米巴块添加到您的模型中,并像往常一样运行dup方法

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    enable
  end
end

class Comment < ActiveRecord::Base
  belongs_to :post
end

class Tag < ActiveRecord::Base
  has_and_belongs_to_many :posts
end

class PostsController < ActionController
  def some_method
    my_post = Post.find(params[:id])
    new_post = my_post.dup
    new_post.save
  end
end

你也可以通过多种方式控制哪些字段被复制,但例如,如果你想防止注释被复制,但你想保持相同的标签,你可以这样做:

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    exclude_field :comments
  end
end

您还可以对字段进行预处理,以帮助指示前缀和后缀以及正则表达式的唯一性。此外,还有许多选项,所以你可以写在最易读的风格为您的目的:

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    include_field :tags
    prepend :title => "Copy of "
    append :contents => " (copied version)"
    regex :contents => {:replace => /dog/, :with => "cat"}
  end
end

关联的递归复制很容易,只要在子模型上启用amoeba即可

class Post < ActiveRecord::Base
  has_many :comments

  amoeba do
    enable
  end
end

class Comment < ActiveRecord::Base
  belongs_to :post
  has_many :ratings

  amoeba do
    enable
  end
end

class Rating < ActiveRecord::Base
  belongs_to :comment
end

配置DSL还有更多选项,所以一定要查看文档。

享受吧!:)

如果你需要一个有关联的深度拷贝,我推荐deep_cloneable gem。