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?*
¿Fue útil?

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top