Ruby-on-rails 复制活动记录记录的最简单方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/60033/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 20:38:25  来源:igfitidea点击:

What is the easiest way to duplicate an activerecord record?

ruby-on-railsrubyrails-activerecord

提问by Brent

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?*

回答by Michael Sepcot

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

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

# 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#cloneto 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 覆盖内置的 Object#clone为您提供一个带有未分配 ID 的新记录(未保存到数据库)。
请注意,它不会复制关联,因此如果需要,您必须手动执行此操作。

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

Rails 3.1 克隆是浅拷贝,请改用 dup ...

回答by Phillip Koebbe

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 simpleexample, 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:

根据您的需要和编程风格,您还可以使用类的新方法和合并的组合。由于缺少一个更好的简单示例,假设您有一个任务安排在某个日期,并且您想将它复制到另一个日期。任务的实际属性并不重要,因此:

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和与原始任务相同的所有其他属性的新任务。使用Task.new,您必须显式调用save,因此如果您希望它自动保存,请将Task.new 更改为Task.create。

Peace.

和平。

回答by Vaughn Draughon

You may also like the Amoeba gemfor ActiveRecord 3.2.

您可能还喜欢ActiveRecord 3.2的Amoeba gem

In your case, you probably want to make use of the nullify, regexor prefixoptions available in the configuration DSL.

在您的情况下,您可能希望使用配置 DSL 中可用的nullify,regexprefix选项。

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

它支持的简单和自动递归重复has_onehas_manyhas_and_belongs_to_many协会,现场预处理和既能对模型和动态应用了灵活而强大的配置DSL。

be sure to check out the Amoeba Documentationbut 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 dupmethod as usual

然后将变形虫块添加到您的模型中并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:

您还可以预处理字段以帮助指示前缀和后缀以及正则表达式的唯一性。此外,还有许多选项,因此您可以根据自己的目的以最易读的风格编写:

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

关联的递归复制很容易,只需在子模型上启用变形虫

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! :)

享受!:)

回答by bradgonesurfing

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

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

回答by Fran?ois Beausoleil

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

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

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

回答by raidfive

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

如果您需要具有关联的深层副本,我推荐deep_cloneablegem。

回答by Foram Thakral

In Rails 5 you can simply create duplicate object or record like this.

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

new_user = old_user.dup

回答by ThienSuBS

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     

回答by Zoran Majstorovic

Here is a sample of overriding ActiveRecord #dupmethod to customize instance duplication and include relation duplication as well:

这是一个覆盖 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

Note: this method doesn't require any external gem but it requires newer ActiveRecord version with #dupmethod implemented

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

回答by Paulo Fidalgo

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:

根据他们的文档示例,对于 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

You create your cloner class:

您创建克隆器类:

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: '[email protected]')>

cloned = UserCloner.call(user, email: '[email protected]')
cloned.persisted?
# => false

cloned.save!
cloned.login
# => nil
cloned.email
# => "[email protected]"

# 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.reject {|k,_v| k.to_s == 'id'}