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

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

也许是这样的:

 new_record = Record.copy(:id)

当前回答

简单的方法是:

#your rails >= 3.1 (i was done it with Rails 5.0.0.1)
  o = Model.find(id)
 # (Range).each do |item|
 (1..109).each do |item|
   new_record = o.dup
   new_record.save
 end

Or

# if your rails < 3.1
 o = Model.find(id)
 (1..109).each do |item|
   new_record = o.clone
   new_record.save
 end     

其他回答

因为可能会有更多的逻辑,当复制一个模型时,我建议创建一个新的类,在那里你可以处理所有需要的逻辑。 为了缓解这种情况,有一种宝石可以帮助你:小丑

根据他们的文档示例,对于User模型:

class User < ActiveRecord::Base
  # create_table :users do |t|
  #  t.string :login
  #  t.string :email
  #  t.timestamps null: false
  # end

  has_one :profile
  has_many :posts
end

你创建你的克隆类:

class UserCloner < Clowne::Cloner
  adapter :active_record

  include_association :profile, clone_with: SpecialProfileCloner
  include_association :posts

  nullify :login

  # params here is an arbitrary Hash passed into cloner
  finalize do |_source, record, params|
    record.email = params[:email]
  end
end

class SpecialProfileCloner < Clowne::Cloner
  adapter :active_record

  nullify :name
end

然后使用它:

user = User.last
#=> <#User(login: 'clown', email: 'clown@circus.example.com')>

cloned = UserCloner.call(user, email: 'fake@example.com')
cloned.persisted?
# => false

cloned.save!
cloned.login
# => nil
cloned.email
# => "fake@example.com"

# associations:
cloned.posts.count == user.posts.count
# => true
cloned.profile.name
# => nil

从项目中复制的例子,但它会给你一个清晰的愿景,你可以实现什么。

简单来说,我想说的是:

Model.new (Model.last.attributes。拒绝{|k,_v| k.to_s == 'id'}

您还可以检查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">

希望这能帮助到你。

下面是一个覆盖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方法

如果你不想复制id,使用ActiveRecord::Base#dup

要获得副本,使用dup(或clone for < rails 3.1+)方法:

#rails >= 3.1
new_record = old_record.dup

# rails < 3.1
new_record = old_record.clone

然后您可以更改任何您想要的字段。

ActiveRecord覆盖了内置的object#克隆,为您提供了一个新的(未保存到DB)记录,该记录具有未分配的ID。 请注意,它不会复制关联,因此如果需要,您必须手动执行此操作。

Rails 3.1克隆是一个浅拷贝,使用dup代替…