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

发布于 2024-07-05 06:03:40 字数 267 浏览 6 评论 0原文

我想制作 ActiveRecord 对象的副本,更改进程中的单个字段(除了 id 之外)。 实现这一目标的最简单方法是什么?

我意识到我可以创建一条新记录,然后迭代每个字段,逐个字段复制数据 - 但我认为必须有一种更简单的方法来做到这一点。

也许是这样的:

 new_record = Record.copy(:id)

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

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.

Perhaps something like this:

 new_record = Record.copy(:id)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

︶葆Ⅱㄣ 2024-07-12 06:03:40

您可能还喜欢 ActiveRecord 3.2 的 Amoeba gem

对于您的情况,您可能希望使用配置 DSL 中提供的 nullifyregexprefix 选项。

它支持has_onehas_manyhas_and_belongs_to_many关联的简单自动递归复制、字段预处理以及可应用的高度灵活且强大的配置DSL无论是对于模型还是在飞行中。

请务必查看 Amoeba 文档,但使用非常简单......

只需

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 还有更多选项,因此请务必查看文档。

享受! :)

You may also like the Amoeba gem for ActiveRecord 3.2.

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

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.

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

just

gem install amoeba

or add

gem 'amoeba'

to your Gemfile

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

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.

Enjoy! :)

记忆で 2024-07-12 06:03:40

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

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

2024-07-12 06:03:40

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

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

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

new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
变身佩奇 2024-07-12 06:03:40

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

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

阿楠 2024-07-12 06:03:40

要获取副本,请使用 dup (或克隆for

#rails >= 3.1
new_record = old_record.dup

# rails < 3.1
new_record = old_record.clone

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

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

Rails 3.1 克隆是浅拷贝,请使用 dup 代替...

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

#rails >= 3.1
new_record = old_record.dup

# rails < 3.1
new_record = old_record.clone

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.

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

[旋木] 2024-07-12 06:03:40

您还可以检查 acts_as_inheritable gem。

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

通过将 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">

希望这可以帮助您。

You can also check the acts_as_inheritable 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."

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

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

Hope this can help you.

第几種人 2024-07-12 06:03:40

由于可能有更多逻辑,因此在复制模型时,我建议创建一个新类,在其中处理所有需要的逻辑。
为了缓解这个问题,有一个 gem 可以提供帮助:clowne

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

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: '[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

从项目复制的示例,但它将给出您可以实现的目标的清晰愿景。

为了快速简单的记录,我会选择:

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

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:

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'}

相思故 2024-07-12 06:03:40

尝试 Rails 的 dup 方法:

new_record = old_record.dup.save

Try rails's dup method:

new_record = old_record.dup.save
倾城泪 2024-07-12 06:03:40

最简单的方法是:

#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     

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     
眼前雾蒙蒙 2024-07-12 06:03:40

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

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

和平。

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:

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.

Peace.

娇妻 2024-07-12 06:03:40

以下是重写 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 方法已实现

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

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 #dup method implemented

眼趣 2024-07-12 06:03:40

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

new_user = old_user.dup

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

new_user = old_user.dup
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文