复制一个activerecord记录最简单的方法是什么?

我想复制一个ActiveRecord对象,在进程中更改一个字段(除了id之外)。要做到这一点,最简单的方法是什么?

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

也许是这样的:

 new_record = Record.copy(:id)
224387 次浏览

要获得副本,使用dup(或克隆<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代替

我通常只是复制属性,改变任何我需要改变的:

new_user = User.new(old_user.attributes.merge(:login => "newlogin"))

根据您的需要和编程风格,还可以结合使用类的新方法和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。

和平。

如果不想复制id,请使用ActiveRecord:: Base #复制品

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

你可能也喜欢ActiveRecord 3.2的变形虫的宝石

在你的情况下,你可能想要使用配置DSL中可用的nullifyregexprefix选项。

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

一定要检查变形虫的文档,但使用非常简单…

只是

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还有更多选项,所以一定要查看文档。

享受吧!:)

你也可以检查acts_as_inheritable宝石。

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">

希望这能帮助到你。

简单的方法是:

#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

# if your rails < 3.1
o = Model.find(id)
(1..109).each do |item|
new_record = o.clone
new_record.save
end
因为可能有更多的逻辑,当复制一个模型时,我建议创建一个新的类,在那里你处理所有需要的逻辑。 为了缓解这种情况,有一个宝石可以帮助:clowne

根据他们的文档示例,对于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.reject {|k,_v| k.to_s == 'id'}

下面是一个重写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

注意:此方法不需要任何外部gem,但需要更新的ActiveRecord版本,实现#dup方法

在Rails 5中,您可以像这样简单地创建重复的对象或记录。

new_user = old_user.dup

尝试rails的dup方法:

new_record = old_record.dup.save