What is the easiest way to duplicate an activereco

2019-01-02 16:36发布

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?

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

10条回答
初与友歌
2楼-- · 2019-01-02 17:06

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.

查看更多
皆成旧梦
3楼-- · 2019-01-02 17:10

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

# 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.

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

查看更多
春风洒进眼中
4楼-- · 2019-01-02 17:12

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

查看更多
君临天下
5楼-- · 2019-01-02 17:15

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

new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
查看更多
刘海飞了
6楼-- · 2019-01-02 17:25

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

查看更多
梦该遗忘
7楼-- · 2019-01-02 17:26

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

查看更多
登录 后发表回答