2008-09-12 19 views
350

Quiero hacer una copia de un registro activerecord, cambiando un solo campo en el proceso (además del id). ¿Cuál es la forma más simple de lograr esto?¿Cuál es la forma más fácil de duplicar un registro de registro activo?

que se dan cuenta de que podía crear un nuevo registro, y luego iterar sobre cada uno de los campos de copiar el campo por campo de datos - pero pensé que debe haber una manera más fácil de hacer esto ...

tales como:

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

Respuesta

539

para obtener una copia, utilice el método clone (o DUP para los carriles 3,1):

# rails < 3.1 
new_record = old_record.clone 

#rails >= 3.1 
new_record = old_record.dup 

a continuación, puede cambiar lo que campos desea.

ActiveRecord overrides the built-in Object#clone para darle 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.

Rails 3.1 clone is a shallow copy, use dup instead...

+6

¿Este siguen trabajando en Rails 3.1.0.beta? Cuando hago 'q = p.clone', y luego' p == q', recupero 'true'. Por otro lado, si uso 'q = p.dup', obtengo' false' de vuelta cuando los comparo. –

+1

Los [documentos de Rails 3.1 en el clon] (http://edgeapi.rubyonrails.org/classes/ActiveResource/Base.html#method-clone) dicen que todavía funciona, pero estoy usando Rails 3.1.0.rc4 e incluso el método 'nuevo?' no está funcionando. – Turadg

+0

Cuando intento clonar y luego guardar en Rails 3.1 RC4, aparece el error 'Mysql2 :: Error: Duplicate entry' en la clave principal. – skattyadz

18

por lo general sólo copiar los atributos, cambiando lo que necesito cambiar:

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

Cuando hago esto, aparece un error de 'atributo desconocido' en una columna debido a una columna que está allí debido a una relación has_many. ¿Hay alguna forma de evitar esto? –

+0

con rails4, no crea una identificación única para el registro – Ben

+2

Para crear un nuevo registro con Rails 4, haga 'User.create (old_user.attributes.merge ({login:" newlogin ", id: nil})) '. Esto guardará a un nuevo usuario con la identificación única correcta. – RajeshM

61

En función de sus necesidades y estilo de programación, también puede utilizar una combinación del nuevo método de la clase y fusionarse. A falta de un mejor simple ejemplo, suponga que tiene una tarea programada para una fecha determinada y desea duplicarla a otra fecha. Los atributos reales de la tarea no son importantes, por lo que:

 
old_task = Task.find(task_id) 
new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date})) 

va a crear una nueva tarea con :id => nil, :scheduled_on => some_new_date, y todos los demás atributos de la misma que la tarea original. Usando Task.new, tendrá que llamar explícitamente a save, por lo que si quiere que se guarde automáticamente, cambie Task.new a Task.create.

Paz.

+5

No estoy seguro de cuán buena idea es que b/c aparece 'ADVERTENCIA: No se pueden asignar en masa los atributos protegidos: id, due_date, created_at, updated_at' devuelto – bcackerman

+0

Gran explicación. – newUserNameHere

+0

Cuando hago esto, obtengo un error de atributo desconocido con una columna debido a una columna que está allí debido a una relación has_many. ¿Hay alguna forma de evitar esto? –

28

Uso ActiveRecord::Base#dup si no desea copiar el ID

+0

.dup no funciona con rails 3.1 con ruby ​​2 – Thorin

+0

@Thorin según la respuesta aceptada anteriormente, parece que el método correcto para Rails <3.1 es '.clone' –

8

Si necesita una copia en profundidad con las asociaciones, recomiendo la gema deep_cloneable.

+0

Yo también. Probé esta joya y funcionó por primera vez, muy fácil de usar. – Rob

31

También le puede interesar el Amoeba gem para ActiveRecord 3.2.

En su caso, es probable que desee utilizar las opciones nullify, regex o prefix disponibles en la configuración DSL.

Soporta fácil y automática la duplicación recursiva de has_one, has_many y has_and_belongs_to_many asociaciones, preprocesamiento de campo y una DSL configuración altamente flexible y potente que se puede aplicar tanto al modelo y sobre la marcha.

asegúrate de revisar el Amoeba Documentation pero el uso es bastante fácil ...

simplemente

gem install amoeba 

o añadir

gem 'amoeba' 

a su Gemfile

continuación, agregue el bloque de ameba a su modelo y ejecute el método dup 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 muchas maneras, pero por ejemplo, si se quería evitar que los comentarios puedan ser duplicados, sino que quería mantener las mismas etiquetas, se podría hacer algo como esto:

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

    amoeba do 
    exclude_field :comments 
    end 
end 

Usted también puede preprocesar campos para ayudar a indicar la singularidad con prefijos y sufijos, así como con expresiones regulares. Además, también hay numerosas opciones para que pueda escribir en el estilo más legible para su 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 

copia recursiva de las asociaciones es fácil, basta con activar la ameba en niños modelos, así

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 revisar la documentación.

¡Disfrútalo! :)

+0

Gran respuesta. Gracias por los detalles! –

+0

¡Gracias, funciona! Pero tengo una pregunta: ¿cómo agrego nuevas entradas con la clonación antes de guardar el objeto clonado? –

0

También puede consultar la gema acts_as_inheritable.

"Actos como heredables" es una gema de Ruby escrita específicamente para los modelos Rails/ActiveRecord.Se debe usar con el Self-Referential Association, o con un modelo que tenga un padre que comparte los atributos heredables. Esto le permitirá heredar cualquier atributo o relación del modelo principal ".

Mediante la adición de acts_as_inheritable a sus modelos, tendrá acceso a estos métodos:

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

Hope esto le puede ayudar.

1

La forma es sencilla:

#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  
Cuestiones relacionadas