回答:
コピーを取得するには、クローン(またはRails 3.1以降の場合はdup)メソッドを使用します。
# rails < 3.1
new_record = old_record.clone
#rails >= 3.1
new_record = old_record.dup
次に、必要なフィールドを変更できます。
ActiveRecordは組み込みのObject#cloneをオーバーライドして、 IDが割り当てられていない新しい(DBに保存されていない)レコードを提供します。
関連付けはコピーされないため、必要に応じて手動でコピーする必要があることに注意してください。
new?
この方法が機能していません。
ニーズとプログラミングスタイルに応じて、クラスの新しいメソッドとマージの組み合わせを使用することもできます。より簡単な例がないので、特定の日付にスケジュールされたタスクがあり、それを別の日付に複製したいとします。タスクの実際の属性は重要ではないため、次のようにします。
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に変更します。
平和。
WARNING: Can't mass-assign protected attributes: id, due_date, created_at, updated_at
返さ
また、ActiveRecord 3.2用のAmoeba gemもお勧めです。
あなたのケースでは、おそらく、設定DSLで利用可能なnullify
、regex
またはprefix
オプションを利用したいでしょう。
それは簡単で、自動再帰の重複をサポートhas_one
、has_many
およびhas_and_belongs_to_many
団体、フィールドの前処理やモデルにその場での両方に適用することができ、非常に柔軟で強力な設定DSLを。
Amoebaのドキュメントを必ずチェックしてください。ただし、使い方は簡単です...
ただ
gem install amoeba
または追加
gem 'amoeba'
あなたのGemfileに
次に、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
コピーするフィールドをさまざまな方法で制御することもできますが、たとえば、コメントが重複しないようにしたいが同じタグを維持したい場合は、次のようにすることができます。
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にはさらに多くのオプションがあるため、ドキュメントを確認してください。
楽しい!:)
.amoeba_dup
だけではなくです.dup
。このコードを実行しようとしましたが、ここでは機能していませんでした。
IDをコピーしたくない場合は、ActiveRecord :: Base#dupを使用します
.clone
私は通常、属性をコピーして、変更が必要なものを変更します。
new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
unknown attribute
と、has_many関係が原因で存在する列があるため、1つの列でエラーが発生します。これを回避する方法はありますか?
User.create(old_user.attributes.merge({ login: "newlogin", id: nil }))
。これにより、新しいユーザーが正しい一意のIDで保存されます。
アソシエーションを含むディープコピーが必要な場合は、deep_cloneable gem をお勧めします。
簡単な方法は次のとおりです。
#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
以下は、#dup
インスタンスの複製をカスタマイズし、リレーションの複製も含めるためにActiveRecord メソッドをオーバーライドするサンプルです。
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を必要としませんが、#dup
メソッドが実装された新しいActiveRecordバージョンが必要です
act_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">
これがお役に立てば幸いです。
ロジックが増える可能性があるため、モデルを複製するときは、必要なすべてのロジックを処理する新しいクラスを作成することをお勧めします。それを容易にするために役立つ宝石があります: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: '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'}
q = p.clone
、それからp == q
私はtrue
戻る。一方、を使用するとq = p.dup
、false
それらを比較すると戻ります。