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

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top