提问



我想复制一条activerecord记录,更改流程中的单个字段(除了 id )。实现这一目标的最简单方法是什么?


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


如:


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

最佳参考


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


# rails < 3.1
new_record = old_record.clone

#rails >= 3.1
new_record = old_record.dup


然后你可以改变你想要的任何字段。


ActiveRecord会覆盖内置的Object#clone,为您提供一个带有未分配ID的新记录(未保存到数据库)。

请注意,它不会复制关联,因此如果需要,您必须手动执行此操作。[29]


Rails 3.1克隆是浅拷贝,而不是使用dup ... [30]

其它参考1


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


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。


和平。

其它参考2


您可能也喜欢ActiveRecord 3.2的Amoeba宝石。[31]


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


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


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


只是


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


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


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


请享用! :)

其它参考3


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

其它参考4


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


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

其它参考5


如果你需要带有关联的深层副本,我推荐使用deep_cloneable gem。[34]

其它参考6


简单的方法是:


#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     

其它参考7


你也可以查看acts_as_inheritable gem。[35]


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


通过向模型添加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">


希望这可以帮到你。

其它参考8


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


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