¿Cuál es la forma más sencilla de duplicar un registro activo?
-
09-06-2019 - |
Pregunta
Quiero hacer una copia de un registro activerecord, cambiando un solo campo en el proceso (además del identificación).¿Cuál es la forma más sencilla de lograr esto?
Me doy cuenta de que podría crear un nuevo registro y luego iterar sobre cada uno de los campos copiando los datos campo por campo, pero pensé que debía haber una manera más fácil de hacer esto...
como:
@newrecord=Record.copy(:id) *perhaps?*
Solución
Para obtener una copia, utilice el método de clonación (o dup para Rails 3.1):
# rails < 3.1
new_record = old_record.clone
#rails >= 3.1
new_record = old_record.dup
Luego puedes cambiar los campos que quieras.
ActiveRecord anula el clon de objeto integrado para brindarle un registro nuevo (no guardado en la base de datos) con una identificación no asignada.
Tenga en cuenta que no copia asociaciones, por lo que tendrá que hacerlo manualmente si es necesario.
El clon de Rails 3.1 es una copia superficial, use dup en su lugar...
Otros consejos
Dependiendo de sus necesidades y estilo de programación, también puede usar una combinación del nuevo método de la clase y fusionar.A falta de una mejor simple Por ejemplo, supongamos que tiene una tarea programada para una fecha determinada y desea duplicarla en otra fecha.Los atributos reales de la tarea no son importantes, por lo tanto:
old_task = Task.find(task_id) new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date}))
creará una nueva tarea con :id => nil
, :scheduled_on => some_new_date
, y todos los demás atributos son los mismos que los de la tarea original.Al utilizar Task.new, tendrá que llamar explícitamente a guardar, por lo que si desea que se guarde automáticamente, cambie Task.new a Task.create.
Paz.
También te puede gustar el gema ameba para ActiveRecord 3.2.
En su caso, probablemente desee utilizar el nullify
, regex
o prefix
opciones disponibles en la configuración DSL.
Admite la duplicación recursiva fácil y automática de has_one
, has_many
y has_and_belongs_to_many
asociaciones, preprocesamiento de campo y una configuración DSL altamente flexible y potente que se puede aplicar tanto al modelo como sobre la marcha.
asegúrese de revisar el Documentación de amebas pero su uso es bastante fácil...
justo
gem install amoeba
o agregar
gem 'amoeba'
a tu archivo de gemas
luego agregue el bloque de ameba a su modelo y ejecute el dup
método como de costumbre
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
También puede controlar qué campos se copian de numerosas maneras, pero, por ejemplo, si desea evitar que se dupliquen los comentarios pero desea mantener las mismas etiquetas, puede hacer algo como esto:
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
exclude_field :comments
end
end
También puede preprocesar campos para ayudar a indicar la unicidad tanto con prefijos como con sufijos, así como con expresiones regulares.Además, también existen numerosas opciones para que puedas escribir en el estilo más legible para tu 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
La copia recursiva de asociaciones es fácil, simplemente habilite la ameba también en los modelos infantiles.
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
La configuración DSL tiene aún más opciones, así que asegúrese de consultar la documentación.
¡Disfrutar!:)
Usar ActiveRecord::Base#dup si no quieres copiar la identificación
Por lo general, solo copio los atributos y cambio lo que sea necesario:
new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
Si necesita una copia profunda con asociaciones, le recomiendo el clonable_profundo joya.
La forma más sencilla es:
#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
O
# if your rails < 3.1
o = Model.find(id)
(1..109).each do |item|
new_record = o.clone
new_record.save
end
En Rails 5 puedes simplemente crear un objeto duplicado o un registro como este.
new_user = old_user.dup
También puedes consultar el actos_como_heredables joya.
"Acts As Inheritable es una gema Ruby escrita específicamente para modelos Rails/ActiveRecord.Está destinado a ser utilizado con el Asociación Autoreferencial, o con un modelo que tiene un padre que comparte los atributos heredables.Esto le permitirá heredar cualquier atributo o relación del modelo principal".
Añadiendo acts_as_inheritable
a tus modelos tendrás acceso a estos métodos:
heredar_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
heredar_relaciones
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 esto le pueda ayudar.
Dado que podría haber más lógica, al duplicar un modelo, sugeriría crear una nueva clase, donde maneje toda la lógica necesaria.Para aliviar eso, hay una joya que puede ayudar: payaso
Según sus ejemplos de documentación, para un modelo de Usuario:
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
Creas tu clase 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
y luego usarlo:
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
Ejemplo copiado del proyecto, pero dará una visión clara de lo que puedes lograr.
Para un registro rápido y sencillo, elegiría:
Model.new(Model.last.attributes.reject {|k,_v| k.to_s == 'id'}
Aquí hay un ejemplo de cómo anular ActiveRecord #dup
Método para personalizar la duplicación de instancias e incluir también la duplicación de relaciones:
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
Nota:este método no requiere ninguna gema externa pero requiere una versión más nueva de ActiveRecord con #dup
método implementado