활성 레코드 레코드를 복제하는 가장 쉬운 방법은 무엇입니까?


412

프로세스에서 단일 필드를 변경하고 ( id 외에도) 활성 레코드 레코드의 사본을 만들고 싶습니다 . 이것을 달성하는 가장 간단한 방법은 무엇입니까?

새 레코드를 만든 다음 필드별로 데이터를 복사하는 각 필드를 반복 할 수 있지만 더 쉬운 방법이 있어야한다고 생각했습니다 ...

같은 :

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

답변:


622

사본을 얻으려면 clone (또는 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에 저장되지 않은) 레코드를 제공합니다.
연결을 복사하지 않으므로 필요한 경우 수동으로이 작업을 수행해야합니다.

Rails 3.1 복제본은 얕은 사본입니다. 대신 dup을 사용하십시오.


6
이것이 여전히 Rails 3.1.0.beta에서 작동합니까? 내가 할 때 q = p.clone, 다음 p == q, 나는 얻을 true다시. 반면에, 내가 사용하는 경우 q = p.dup, 내가 수 false를 비교할 때 다시.
Autumnsault

1
복제본Rails 3.1 문서는 여전히 작동한다고 말하지만 Rails 3.1.0.rc4를 사용하고 있으며 new?방법 조차도 작동하지 않습니다.
Turadg

12
이 기능은 DUP으로 대체 된 것 같습니다 gist.github.com/994614
skattyadz

74
반드시 클론을 사용하지 마십시오. 다른 포스터에서 언급했듯이 clone 메소드는 이제 Kernel # clone을 사용하여 id를 복사합니다. 지금부터 ActiveRecord :: Base # dup 사용
bradgonesurfing

5
나는 이것이 진짜 고통이었다고 말해야한다. 이와 같이 간단한 기능을 의도 한 기능으로 변경하면 사양 범위가 충분하지 않은 경우 일부 중요한 기능이 손상 될 수 있습니다.
Matt Smith

74

필요와 프로그래밍 스타일에 따라 클래스의 새로운 메소드와 병합의 조합을 사용할 수도 있습니다. 더 간단한 예가 없으면 특정 날짜로 예약 된 작업이 있고 다른 날짜로 복제하려고한다고 가정하십시오. 작업의 실제 속성은 중요하지 않으므로 다음과 같이하십시오.

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로 변경하십시오.

평화.


5
확실히 당신이 얻을 c를 아이디어의 좋은이 B는 / 얼마나 WARNING: Can't mass-assign protected attributes: id, due_date, created_at, updated_at반환
bcackerman

이렇게하면 has_many 관계로 인해 열이 있기 때문에 하나의 열에 알 수없는 속성 오류가 발생합니다. 이 주위에 어떤 방법이 있습니까?
Ruben Martinez Jr.

2
Ru 나는 이것이 오래된 게시물이라는 것을 알고 있지만 네, 속성 해시에 '.except'를 사용 하여이 문제를 해결할 수 있습니다 : new_task = Task.new (old_task.attributes.except (: attribute_you_dont_want, : another_aydw) .merge ({: scheduled_on => some_new_date}))
니니기

@PhillipKoebbe 감사합니다-그러나 ID가 null이 아닌 경우 어떻게해야합니까? 중복을 만들 때 레일이 자동으로 새 ID를 할당하고 싶습니다. 이것이 가능합니까?
BKSpurgeon

1
old_task.attribtes는 유감스럽게도 ID 필드를 할당합니다. 그것은 나를 위해 작동하지 않습니다
BKSpurgeon

32

ActiveRecord 3.2 용 Amoeba gem 을 좋아할 수도 있습니다 .

귀하의 경우에는, 당신은 아마 사용하게 할 nullify, regex또는 prefix구성 DSL에서 사용할 수있는 옵션을.

그것은 간단하고 자동 순환 중복 지원 has_one, has_manyhas_and_belongs_to_many협회, 필드 전처리 및 모델에와 비행에 모두 적용 할 수있는 매우 유연하고 강력한 구성 DSL을.

Amoeba Documentation 을 확인해야 하지만 사용법은 매우 쉽습니다.

다만

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에는 더 많은 옵션이 있으므로 설명서를 확인하십시오.

즐겨! :)


좋은 대답입니다. 세부 감사합니다!
Derek Prior

고마워요 !! 그러나 복제 된 객체를 저장하기 전에 복제로 새로운 항목을 추가하는 방법에 대한 질문이 하나 있습니까?
Mohd Anas

1
여기서 수정하십시오. 올바른 방법은 .amoeba_dup아닙니다 .dup. 이 코드를 실행하려고했지만 여기에서 작동하지 않았습니다.
빅터


24

나는 일반적으로 속성을 복사하여 변경해야 할 사항을 변경합니다.

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

이렇게하면 unknown attributehas_many 관계로 인해 열이 있기 때문에 하나의 열에 오류가 발생합니다. 이 주위에 어떤 방법이 있습니까?
Ruben Martinez Jr.

rails4를 사용하면 레코드의 고유 ID를 만들지 않습니다.
Ben

4
Rails 4를 사용하여 새 레코드를 만들려면을 수행하십시오 User.create(old_user.attributes.merge({ login: "newlogin", id: nil })). 올바른 고유 ID를 가진 새 사용자를 저장합니다.
RajeshM

Rails에는 Hash # exceptHash # slice가 있어 제안 된 방법이 가장 강력하고 오류 발생 가능성이 적습니다. 확장이 쉬운 추가 라이브러리를 추가 할 필요가 없습니다.
kucaahbe

10

연관성이있는 깊은 사본이 필요한 경우 deep_cloneable gem을 사용하는 것이 좋습니다 .


나도. 나는이 보석을 시험해 보았고 처음에는 매우 사용하기 쉬웠습니다.
Rob

4

Rails 5에서는 이와 같이 중복 객체 또는 레코드를 만들 수 있습니다.

new_user = old_user.dup

2

쉬운 방법은 다음과 같습니다

#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     

2

다음은 #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

참고 :이 방법은 외부 보석이 필요하지 않지만 #dup방법이 구현 된 최신 ActiveRecord 버전이 필요합니다


0

acts_as_inheritable gem을 확인할 수도 있습니다 .

"상속 할 수없는 행위는 Rails / ActiveRecord 모델을 위해 특별히 작성된 루비 보석입니다. 자체 참조 연관 또는 상속 가능한 속성을 공유하는 부모가있는 모델과 함께 사용하기위한 것입니다.이를 통해 모든 속성 또는 부모 모델과의 관계 "

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

이것이 당신을 도울 수 있기를 바랍니다.


0

더 많은 논리가있을 수 있으므로 모델을 복제 할 때 필요한 모든 논리를 처리하는 새 클래스를 만드는 것이 좋습니다. 그것을 돕기 위해, 도움이 될 수있는 보석이 있습니다 : 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'}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.