Qual é a maneira mais fácil de duplicar um registro ativo?
-
09-06-2019 - |
Pergunta
Quero fazer uma cópia de um registro do activerecord, alterando um único campo no processo (além do eu ia).Qual é a maneira mais simples de conseguir isso?
Sei que poderia criar um novo registro e, em seguida, iterar sobre cada um dos campos, copiando os dados campo por campo - mas imaginei que deveria haver uma maneira mais fácil de fazer isso...
como:
@newrecord=Record.copy(:id) *perhaps?*
Solução
Para obter uma cópia, use o método clone (ou dup para Rails 3.1):
# rails < 3.1
new_record = old_record.clone
#rails >= 3.1
new_record = old_record.dup
Então você pode alterar os campos que desejar.
ActiveRecord substitui o Object#clone integrado para fornecer um novo registro (não salvo no banco de dados) com um ID não atribuído.
Observe que ele não copia associações, então você terá que fazer isso manualmente se precisar.
O clone do Rails 3.1 é uma cópia superficial, use dup em vez disso ...
Outras dicas
Dependendo de suas necessidades e estilo de programação, você também pode usar uma combinação do novo método de classe e mesclagem.Por falta de um melhor simples Por exemplo, suponha que você tenha uma tarefa agendada para uma determinada data e queira duplicá-la para outra data.Os atributos reais da tarefa não são importantes, então:
old_task = Task.find(task_id) new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date}))
criará uma nova tarefa com :id => nil
, :scheduled_on => some_new_date
, e todos os outros atributos iguais aos da tarefa original.Usando Task.new, você terá que chamar explicitamente save, então se quiser que ele seja salvo automaticamente, altere Task.new para Task.create.
Paz.
Você também pode gostar do Gema de ameba para ActiveRecord 3.2.
No seu caso, você provavelmente desejará usar o nullify
, regex
ou prefix
opções disponíveis na configuração DSL.
Ele suporta duplicação recursiva fácil e automática de has_one
, has_many
e has_and_belongs_to_many
associações, pré-processamento de campo e uma configuração DSL altamente flexível e poderosa que pode ser aplicada tanto ao modelo quanto em tempo real.
não deixe de conferir o Documentação sobre ameba mas o uso é bem fácil...
apenas
gem install amoeba
ou adicione
gem 'amoeba'
para o seu Gemfile
em seguida, adicione o bloco de ameba ao seu modelo e execute o dup
método como de costume
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
Você também pode controlar quais campos são copiados de várias maneiras, mas, por exemplo, se quiser evitar que os comentários sejam duplicados, mas quiser manter as mesmas tags, você pode fazer algo assim:
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
exclude_field :comments
end
end
Você também pode pré-processar campos para ajudar a indicar a exclusividade com prefixos e sufixos, bem como com expressões regulares.Além disso, também existem inúmeras opções para que você possa escrever no estilo mais legível para o seu propósito:
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
A cópia recursiva de associações é fácil, basta ativar o ameba também nos modelos filhos
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
A configuração DSL possui ainda mais opções, então não deixe de conferir a documentação.
Aproveitar!:)
Usar ActiveRecord::Base#dup se você não quiser copiar o id
Normalmente apenas copio os atributos, alterando o que preciso:
new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
Se você precisar de uma cópia detalhada com associações, recomendo o deep_cloneable gema.
A maneira mais fácil é:
#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
Ou
# if your rails < 3.1
o = Model.find(id)
(1..109).each do |item|
new_record = o.clone
new_record.save
end
No Rails 5 você pode simplesmente criar objetos duplicados ou registros como este.
new_user = old_user.dup
Você também pode verificar o atua_como_inheritável gema.
"Acts As Inheritable é uma Gem Ruby escrita especificamente para modelos Rails/ActiveRecord.Destina-se a ser usado com o Associação Auto-Referencial, ou com um modelo que tenha um pai que compartilhe os atributos herdáveis.Isso permitirá que você herde qualquer atributo ou relação do modelo pai."
Adicionando acts_as_inheritable
aos seus modelos, você terá acesso a estes métodos:
herdar_atributos
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
relações_herdadas
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">
Espero que isso possa ajudá-lo.
Como poderia haver mais lógica, ao duplicar um modelo, sugiro criar uma nova classe, onde você lide com toda a lógica necessária.Para facilitar isso, há uma joia que pode ajudar: palhaço
De acordo com os exemplos de documentação, para um modelo de usuário:
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
Você cria sua classe clonadora:
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
e então use-o:
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
Exemplo copiado do projeto, mas que dará uma visão clara do que você pode alcançar.
Para um registro rápido e simples, eu usaria:
Model.new(Model.last.attributes.reject {|k,_v| k.to_s == 'id'}
Aqui está um exemplo de substituição do ActiveRecord #dup
método para personalizar a duplicação de instâncias e incluir também a duplicação de relações:
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
Observação:este método não requer nenhuma gema externa, mas requer uma versão mais recente do ActiveRecord com #dup
método implementado