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

时间:2022-09-11 17:35:39

I want to make a copy of an activerecord record, changing a single field in the process (in addition to the id). What is the simplest way to accomplish this?

我想复制一个activerecord记录,在进程中更改一个字段(除了id)。最简单的方法是什么?

I realize I could create a new record, and then iterate over each of the fields copying the data field-by-field - but I figured there must be an easier way to do this...

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

such as:

如:

 @newrecord=Record.copy(:id)  *perhaps?*

9 个解决方案

#1


550  

To get a copy, use the clone (or dup for rails 3.1) method:

要获得副本,请使用克隆(或rails 3.1中的dup)方法:

# rails < 3.1
new_record = old_record.clone

#rails >= 3.1
new_record = old_record.dup

Then you can change whichever fields you want.

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

ActiveRecord overrides the built-in Object#clone to give you a new (not saved to the DB) record with an unassigned ID.
Note that it does not copy associations, so you'll have to do this manually if you need to.

ActiveRecord重写了内置的对象#克隆,以给您一个新的(未保存到DB)记录和一个未分配的ID。

Rails 3.1 clone is a shallow copy, use dup instead...

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

#2


63  

Depending on your needs and programming style, you can also use a combination of the new method of the class and merge. For lack of a better simple example, suppose you have a task scheduled for a certain date and you want to duplicate it to another date. The actual attributes of the task aren't important, so:

根据您的需要和编程风格,您还可以使用类的新方法和merge的组合。如果没有更好的简单示例,假设您有一个为特定日期安排的任务,并且希望将其复制到另一个日期。任务的实际属性并不重要,因此:

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

will create a new task with :id => nil, :scheduled_on => some_new_date, and all other attributes the same as the original task. Using Task.new, you will have to explicitly call save, so if you want it saved automatically, change Task.new to Task.create.

将使用:id => nil,:scheduled_on => some_new_date创建一个新任务,以及与原始任务相同的所有其他属性。使用任务。新的,您将必须显式地调用save,因此如果您希望它自动保存,请更改任务。新Task.create。

Peace.

和平。

#3


31  

You may also like the Amoeba gem for ActiveRecord 3.2.

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

In your case, you probably want to make use of the nullify, regex or prefix options available in the configuration DSL.

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

It supports easy and automatic recursive duplication of has_one, has_many and has_and_belongs_to_many associations, field preprocessing and a highly flexible and powerful configuration DSL that can be applied both to the model and on the fly.

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

be sure to check out the Amoeba Documentation but usage is pretty easy...

一定要查看Amoeba文档,但是使用起来很容易……

just

只是

gem install amoeba

or add

或添加

gem 'amoeba'

to your Gemfile

你Gemfile

then add the amoeba block to your model and run the dup method as usual

然后将amoeba块添加到模型中,并像往常一样运行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

You can also control which fields get copied in numerous ways, but for example, if you wanted to prevent comments from being duplicated but you wanted to maintain the same tags, you could do something like this:

您还可以通过多种方式控制哪些字段被复制,但是,如果您想防止注释被复制,但是您想维护相同的标记,您可以这样做:

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

  amoeba do
    exclude_field :comments
  end
end

You can also preprocess fields to help indicate uniqueness with both prefixes and suffixes as well as regexes. In addition, there are also numerous options so you can write in the most readable style for your purpose:

您还可以预先处理字段,以帮助指示前缀和后缀以及regex的惟一性。此外,还有很多选项,你可以用最易读的风格来写作:

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

Recursive copying of associations is easy, just enable amoeba on child models as well

关联的递归复制很简单,只需在子模型上启用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

The configuration DSL has yet more options, so be sure to check out the documentation.

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

Enjoy! :)

享受吧!:)

#4


28  

Use ActiveRecord::Base#dup if you don't want to copy the id

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

#5


19  

I usually just copy the attributes, changing whatever I need changing:

我通常只复制属性,更改我需要更改的内容:

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

#6


8  

If you need a deep copy with associations, I recommend the deep_cloneable gem.

如果您需要一个具有关联的深度副本,我推荐deep_cloneable gem。

#7


1  

The easily way is:

最容易的方法是:

#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     

#8


0  

You can also check the acts_as_inheritable gem.

您还可以检查acts_as_inheritance gem。

"Acts As Inheritable is a Ruby Gem specifically written for Rails/ActiveRecord models. It is meant to be used with the Self-Referential Association, or with a model having a parent that share the inheritable attributes. This will let you inherit any attribute or relation from the parent model."

“充当可继承的角色是专门为Rails/ActiveRecord模型编写的Ruby Gem。”它应该与自引用关联一起使用,或者与具有共享可继承属性的父类的模型一起使用。这将允许您从父模型继承任何属性或关系。

By adding acts_as_inheritable to your models you will have access to these methods:

通过向模型添加acts_as_inheritance,您可以访问这些方法:

inherit_attributes

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

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

Hope this can help you.

希望这能帮到你。

#9


0  

Since there could be more logic, when duplicating a model, I would suggest to create a new class, where you handle all the needed logic. To ease that, there's a gem that can help: clowne

由于可以有更多的逻辑,所以在复制模型时,我建议创建一个新的类,在这个类中您可以处理所有需要的逻辑。为了缓解这个问题,有一颗宝石可以帮助你:小丑

As per their documentation examples, for a User model:

根据他们的文档示例,为用户模型:

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

You create your cloner class:

你创建cloner类:

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

and then use it:

然后使用它:

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

Example copied from the project, but it will give a clear vision of what you can achieve.

示例从项目中复制,但它将给出一个清晰的远景,您可以实现什么。

For a quick and simple record I would go with:

为了快速而简单的记录,我将:

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

Model.new(Model.last.attributes。反对{ | k,_v | k。to_s = =“id”}

#1


550  

To get a copy, use the clone (or dup for rails 3.1) method:

要获得副本,请使用克隆(或rails 3.1中的dup)方法:

# rails < 3.1
new_record = old_record.clone

#rails >= 3.1
new_record = old_record.dup

Then you can change whichever fields you want.

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

ActiveRecord overrides the built-in Object#clone to give you a new (not saved to the DB) record with an unassigned ID.
Note that it does not copy associations, so you'll have to do this manually if you need to.

ActiveRecord重写了内置的对象#克隆,以给您一个新的(未保存到DB)记录和一个未分配的ID。

Rails 3.1 clone is a shallow copy, use dup instead...

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

#2


63  

Depending on your needs and programming style, you can also use a combination of the new method of the class and merge. For lack of a better simple example, suppose you have a task scheduled for a certain date and you want to duplicate it to another date. The actual attributes of the task aren't important, so:

根据您的需要和编程风格,您还可以使用类的新方法和merge的组合。如果没有更好的简单示例,假设您有一个为特定日期安排的任务,并且希望将其复制到另一个日期。任务的实际属性并不重要,因此:

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

will create a new task with :id => nil, :scheduled_on => some_new_date, and all other attributes the same as the original task. Using Task.new, you will have to explicitly call save, so if you want it saved automatically, change Task.new to Task.create.

将使用:id => nil,:scheduled_on => some_new_date创建一个新任务,以及与原始任务相同的所有其他属性。使用任务。新的,您将必须显式地调用save,因此如果您希望它自动保存,请更改任务。新Task.create。

Peace.

和平。

#3


31  

You may also like the Amoeba gem for ActiveRecord 3.2.

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

In your case, you probably want to make use of the nullify, regex or prefix options available in the configuration DSL.

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

It supports easy and automatic recursive duplication of has_one, has_many and has_and_belongs_to_many associations, field preprocessing and a highly flexible and powerful configuration DSL that can be applied both to the model and on the fly.

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

be sure to check out the Amoeba Documentation but usage is pretty easy...

一定要查看Amoeba文档,但是使用起来很容易……

just

只是

gem install amoeba

or add

或添加

gem 'amoeba'

to your Gemfile

你Gemfile

then add the amoeba block to your model and run the dup method as usual

然后将amoeba块添加到模型中,并像往常一样运行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

You can also control which fields get copied in numerous ways, but for example, if you wanted to prevent comments from being duplicated but you wanted to maintain the same tags, you could do something like this:

您还可以通过多种方式控制哪些字段被复制,但是,如果您想防止注释被复制,但是您想维护相同的标记,您可以这样做:

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

  amoeba do
    exclude_field :comments
  end
end

You can also preprocess fields to help indicate uniqueness with both prefixes and suffixes as well as regexes. In addition, there are also numerous options so you can write in the most readable style for your purpose:

您还可以预先处理字段,以帮助指示前缀和后缀以及regex的惟一性。此外,还有很多选项,你可以用最易读的风格来写作:

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

Recursive copying of associations is easy, just enable amoeba on child models as well

关联的递归复制很简单,只需在子模型上启用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

The configuration DSL has yet more options, so be sure to check out the documentation.

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

Enjoy! :)

享受吧!:)

#4


28  

Use ActiveRecord::Base#dup if you don't want to copy the id

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

#5


19  

I usually just copy the attributes, changing whatever I need changing:

我通常只复制属性,更改我需要更改的内容:

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

#6


8  

If you need a deep copy with associations, I recommend the deep_cloneable gem.

如果您需要一个具有关联的深度副本,我推荐deep_cloneable gem。

#7


1  

The easily way is:

最容易的方法是:

#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     

#8


0  

You can also check the acts_as_inheritable gem.

您还可以检查acts_as_inheritance gem。

"Acts As Inheritable is a Ruby Gem specifically written for Rails/ActiveRecord models. It is meant to be used with the Self-Referential Association, or with a model having a parent that share the inheritable attributes. This will let you inherit any attribute or relation from the parent model."

“充当可继承的角色是专门为Rails/ActiveRecord模型编写的Ruby Gem。”它应该与自引用关联一起使用,或者与具有共享可继承属性的父类的模型一起使用。这将允许您从父模型继承任何属性或关系。

By adding acts_as_inheritable to your models you will have access to these methods:

通过向模型添加acts_as_inheritance,您可以访问这些方法:

inherit_attributes

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

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

Hope this can help you.

希望这能帮到你。

#9


0  

Since there could be more logic, when duplicating a model, I would suggest to create a new class, where you handle all the needed logic. To ease that, there's a gem that can help: clowne

由于可以有更多的逻辑,所以在复制模型时,我建议创建一个新的类,在这个类中您可以处理所有需要的逻辑。为了缓解这个问题,有一颗宝石可以帮助你:小丑

As per their documentation examples, for a User model:

根据他们的文档示例,为用户模型:

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

You create your cloner class:

你创建cloner类:

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

and then use it:

然后使用它:

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

Example copied from the project, but it will give a clear vision of what you can achieve.

示例从项目中复制,但它将给出一个清晰的远景,您可以实现什么。

For a quick and simple record I would go with:

为了快速而简单的记录,我将:

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

Model.new(Model.last.attributes。反对{ | k,_v | k。to_s = =“id”}