Bir Activerecord kaydını çoğaltmanın en kolay yolu nedir?


412

İşlemdeki tek bir alanı değiştirerek ( kimliğe ek olarak ) bir activerecord kaydının bir kopyasını yapmak istiyorum . Bunu başarmanın en basit yolu nedir?

Yeni bir kayıt oluşturabileceğimin farkındayım ve daha sonra verileri alanlara göre kopyalayarak alanların her birini yineliyorum - ancak bunu yapmanın daha kolay bir yolu olması gerektiğini düşündüm ...

gibi:

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

Yanıtlar:


622

Bir kopyasını almak için klonlama (veya rails 3.1+ için dup) yöntemini kullanın:

# rails < 3.1
new_record = old_record.clone

#rails >= 3.1
new_record = old_record.dup

Ardından, istediğiniz alanları değiştirebilirsiniz.

ActiveRecord, atanmamış bir kimliğe sahip yeni bir (DB'ye kaydedilmemiş) kayıt vermek için yerleşik Object # klonunu geçersiz kılar .
İlişkileri kopyalamadığını unutmayın, bu nedenle gerekirse bunu manuel olarak yapmanız gerekir.

Raylar 3.1 klon sığ bir kopyadır, bunun yerine dup kullanın ...


6
Bu hala Rails 3.1.0.beta'da çalışıyor mu? Ben ne zaman q = p.cloneve ardından p == q, benim hemen truegeri. Öte yandan, eğer kullanırsam q = p.dup, falseonları karşılaştırırken geri dönerim.
Autumnsault

1
Raylar klonu üzerinde 3.1 dokümanlar hala çalışıyor söylüyorlar ama ben Raylar 3.1.0.rc4 kullanıyorum ve hatta new?yöntem çalışmıyor.
Turadg

12
Bu işlevin yerini dup gibi aldı: gist.github.com/994614
skattyadz

74
Kesinlikle klon KULLANMAYIN. Diğer posterlerin de belirttiği gibi, klon yöntemi şimdi kimliği kopyalayacak olan Çekirdek # klonunu kullanmaya yetkilidir. ActiveRecord :: Base #
dup'ı

5
Söylemeliyim ki, bu gerçek bir acıydı. Amaçlanan işlevsellikte böyle basit bir değişiklik, iyi teknik özellik kapsamına sahip değilseniz bazı önemli özellikleri sakatlayabilir.
Matt Smith

74

İhtiyaçlarınıza ve programlama stilinize bağlı olarak, yeni sınıf ve birleştirme yönteminin bir kombinasyonunu da kullanabilirsiniz. Daha basit bir örnek olmaması için, belirli bir tarih için zamanlanmış bir göreviniz olduğunu ve bunu başka bir tarihe çoğaltmak istediğinizi varsayalım. Görevin gerçek özellikleri önemli değildir, bu nedenle:

old_task = Görev.find (görev_kimliği)
new_task = Görev.yeni (old_task.attributes.merge ({: planned_on => bazı_yeni_tarihi}))

ile yeni bir görev yaratacak :id => nil, :scheduled_on => some_new_dateözgün görev olarak aynı, ve diğer tüm özelliklerini. Task.new kullanarak, açıkça kaydet'i çağırmanız gerekir, bu nedenle otomatik olarak kaydedilmesini istiyorsanız Task.new öğesini Task.create olarak değiştirin.

Barış.


5
Ne kadar iyi bir fikir bu b / c WARNING: Can't mass-assign protected attributes: id, due_date, created_at, updated_atdöndüğünüzden emin değilim
bcackerman

Bunu yaptığımda, bir has_many ilişkisi nedeniyle var olan bir sütun nedeniyle bir sütun ile bilinmeyen bir öznitelik hatası alıyorum. Bunun etrafında bir yol var mı?
Ruben Martinez Jr.5

2
@RubenMartineJr. Bu eski bir yazı olduğunu biliyorum, ama evet hash: new_task = Task.new (old_task.attributes.except (: attribute_you_dont_want,: another_aydw) .merge ({attribute_you_dont_want,: another_aydw). => some_new_date}))
Ninigi

@PhillipKoebbe teşekkür ederim - ama kimliğin boş olmamasını istersem ne olur? Yinelenen oluştururken raylar otomatik olarak yeni bir kimlik atamak istiyorum - bu mümkün mü?
BKSpurgeon

1
old_task.attribtes maalesef kimlik alanını da atar. Benim için çalışmıyor
BKSpurgeon

32

ActiveRecord 3.2 için Amip taşını da sevebilirsiniz .

Senin durumunda, muhtemelen yararlanmak isteyen nullify, regexya da prefixyapılandırma DSL bulunan seçenekler.

Bu kolay ve otomatik özyinelemeli tekrarını destekler has_one, has_manyve has_and_belongs_to_manydernek, saha preprocessing ve modele ve anında hem uygulanabilir son derece esnek ve güçlü yapılandırma DSL.

Amip Belgeleri'ni kontrol ettiğinizden emin olun, ancak kullanımı oldukça kolaydır ...

sadece

gem install amoeba

veya ekle

gem 'amoeba'

Gemfile'nıza

ardından modelinize amip bloğunu ekleyin ve dupyöntemi her zamanki gibi çalıştırın

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

Ayrıca, hangi alanların çeşitli yollarla kopyalanacağını da kontrol edebilirsiniz, ancak örneğin, yorumların çoğaltılmasını önlemek istiyorsanız, ancak aynı etiketleri korumak istiyorsanız, şöyle bir şey yapabilirsiniz:

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    exclude_field :comments
  end
end

Ayrıca, hem önek hem de soneklerin yanı sıra normal ifadelerle benzersizliği belirtmeye yardımcı olması için alanları önceden işleyebilirsiniz. Ayrıca, amacınız için en okunabilir tarzda yazabilmeniz için çok sayıda seçenek vardır:

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

Derneklerin yinelemeli kopyalanması kolaydır, sadece çocuk modellerinde amip etkinleştirin

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 yapılandırmasında daha fazla seçenek var, bu nedenle belgelere baktığınızdan emin olun.

Zevk almak! :)


Mükemmel cevap. Detay için teşekkürler!
Derek Prior

Teşekkürler çalışıyor !! Ama bir soru var nasıl klonlanmış nesne kaydetmeden önce klonlama ile yeni girişler eklemek?
Mohd Anas

1
Burada bir düzeltme. Doğru yöntem, .amoeba_dupsadece değil .dup. Bu kodu yürütmeye çalışıyordum, ama burada çalışmıyordu.
Victor


24

Genellikle sadece özellikleri değiştirerek, neye ihtiyacım olursa olsun değiştiriyorum:

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

Bunu yaptığımda, unknown attributebir has_many ilişkisi nedeniyle var olan bir sütun nedeniyle bir sütunla ilgili bir hata alıyorum. Bunun etrafında bir yol var mı?
Ruben Martinez Jr.5

rails4 ile kayıt için benzersiz bir kimlik oluşturmaz
Ben

4
Rails 4 ile yeni bir kayıt oluşturmak için yapın User.create(old_user.attributes.merge({ login: "newlogin", id: nil })). Bu, doğru benzersiz kimliğe sahip yeni bir kullanıcıyı kaydeder.
RajeshM

Rails, Hash # ve Hash # dilimi dışında , potansiyel olarak önerilen yöntemi en güçlü ve daha az hataya eğilimli hale getirir. Ek libs eklemeye gerek yok, uzatılması kolay.
kucaahbe


4

Rails 5'te, sadece yinelenen nesne oluşturabilir veya böyle kayıt yapabilirsiniz.

new_user = old_user.dup

2

Kolay yol:

#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

Veya

# if your rails < 3.1
 o = Model.find(id)
 (1..109).each do |item|
   new_record = o.clone
   new_record.save
 end     

2

#dupÖrnek çoğaltmayı özelleştirmek ve ilişki çoğaltmasını da dahil etmek için ActiveRecord yöntemini geçersiz kılma örneği:

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

Not: Bu yöntem herhangi bir harici taş gerektirmez, ancak #dupyöntem uygulanmış daha yeni ActiveRecord sürümünü gerektirir


0

Ayrıca act_as_inheritable öğesini de kontrol edebilirsiniz. .

"Eylemleri gibi devralınabilen özellikle Raylar / ActiveRecord modelleri için yazılmış bir Ruby Gem. O kullanılmak üzere içindir Kendinden Referans Derneği ile veya devralınan nitelikleri paylaşan bir üst sahip bir modelle . Bu, herhangi bir özelliği devralmanıza veya ana modelden ilişki. "

acts_as_inheritableModellerinize ekleyerek şu yöntemlere erişebilirsiniz:

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

Umarım bu size yardımcı olabilir.


0

Daha fazla mantık olabileceğinden, bir modeli çoğaltırken, gerekli tüm mantığı işlediğiniz yeni bir sınıf oluşturmanızı öneririm. Bunu kolaylaştırmak için yardımcı olabilecek bir mücevher var: palyaço

Bir Kullanıcı modeli için dokümantasyon örneklerine göre:

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

Cloner sınıfınızı oluşturursunuz:

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

ve sonra kullanın:

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

Örnek projeden kopyalanmıştır, ancak neyi başarabileceğiniz konusunda net bir vizyon verecektir.

Hızlı ve basit bir kayıt için:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.