Qual è il modo più semplice per duplicare un record activerecord?
-
09-06-2019 - |
Domanda
Voglio fare una copia di un record activerecord, modificando un singolo campo nel processo (oltre al file id).Qual è il modo più semplice per raggiungere questo obiettivo?
Mi rendo conto che potrei creare un nuovo record e quindi scorrere ciascuno dei campi copiando i dati campo per campo, ma ho pensato che ci dovesse essere un modo più semplice per farlo...
ad esempio:
@newrecord=Record.copy(:id) *perhaps?*
Soluzione
Per ottenerne una copia, utilizza il metodo clone (o dup per rails 3.1):
# rails < 3.1
new_record = old_record.clone
#rails >= 3.1
new_record = old_record.dup
Quindi puoi modificare i campi che desideri.
ActiveRecord sovrascrive il clone Object# incorporato per darti un nuovo record (non salvato nel DB) con un ID non assegnato.
Tieni presente che non copia le associazioni, quindi dovrai farlo manualmente se necessario.
Il clone di Rails 3.1 è una copia superficiale, usa invece dup...
Altri suggerimenti
A seconda delle tue esigenze e del tuo stile di programmazione, puoi anche utilizzare una combinazione del nuovo metodo della classe e dell'unione.In mancanza di meglio semplice Ad esempio, supponiamo di avere un'attività pianificata per una determinata data e di volerla duplicare in un'altra data.Gli attributi effettivi dell'attività non sono importanti, quindi:
old_task = Task.find(task_id) new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date}))
creerà una nuova attività con :id => nil
, :scheduled_on => some_new_date
, e tutti gli altri attributi sono uguali a quelli dell'attività originale.Usando Task.new, dovrai chiamare esplicitamente save, quindi se vuoi che venga salvato automaticamente, cambia Task.new in Task.create.
Pace.
Potrebbe piacerti anche il Gemma dell'ameba per ActiveRecord 3.2.
Nel tuo caso, probabilmente vorrai utilizzare il file nullify
, regex
O prefix
opzioni disponibili nella configurazione DSL.
Supporta la duplicazione ricorsiva semplice e automatica di has_one
, has_many
E has_and_belongs_to_many
associazioni, preelaborazione sul campo e una configurazione DSL altamente flessibile e potente che può essere applicata sia al modello che al volo.
assicurati di controllare il Documentazione sull'ameba ma l'utilizzo è piuttosto semplice...
Appena
gem install amoeba
o aggiungi
gem 'amoeba'
al tuo Gemfile
quindi aggiungi il blocco ameba al tuo modello ed esegui il file dup
metodo come al solito
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
Puoi anche controllare quali campi vengono copiati in numerosi modi, ma ad esempio, se desideri evitare che i commenti vengano duplicati ma desideri mantenere gli stessi tag, potresti fare qualcosa del genere:
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
exclude_field :comments
end
end
Puoi anche preelaborare i campi per indicare l'unicità sia con i prefissi che con i suffissi e con le espressioni regolari.Inoltre, ci sono anche numerose opzioni per poter scrivere nello stile più leggibile per il tuo scopo:
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 ricorsiva delle associazioni è semplice, basta abilitare l'ameba anche sui modelli figlio
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 configurazione DSL ha ancora più opzioni, quindi assicurati di controllare la documentazione.
Godere!:)
Utilizzo ActiveRecord::Base#dup se non vuoi copiare l'id
Di solito copio semplicemente gli attributi, cambiando tutto ciò di cui ho bisogno di cambiare:
new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
Se hai bisogno di una copia approfondita con le associazioni, ti consiglio il deep_cloneable gemma.
Il modo più semplice è:
#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
In Rails 5 puoi semplicemente creare oggetti duplicati o registrare come questo.
new_user = old_user.dup
Puoi anche controllare il agisce_come_ereditabile gemma.
"Acts As Inheritable è una gemma di rubino scritta appositamente per i modelli Rails/ActiveRecord.È pensato per essere utilizzato con Associazione Autoreferenziale, o con un modello avente un genitore che condivide gli attributi ereditabili.Ciò ti consentirà di ereditare qualsiasi attributo o relazione dal modello principale."
Aggiungendo acts_as_inheritable
ai tuoi modelli avrai accesso a questi metodi:
eredita_attributi
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
eredita_relazioni
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">
Spero che questo possa aiutarti.
Poiché potrebbe esserci più logica, quando si duplica un modello, suggerirei di creare una nuova classe, in cui gestire tutta la logica necessaria.Per facilitarlo, c'è una gemma che può aiutare: clown
Secondo i loro esempi di documentazione, per un modello Utente:
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
Crei la tua classe cloner:
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 poi 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
Esempio copiato dal progetto, ma darà una visione chiara di ciò che puoi ottenere.
Per un record semplice e veloce sceglierei:
Model.new(Model.last.attributes.reject {|k,_v| k.to_s == 'id'}
Ecco un esempio di sostituzione di ActiveRecord #dup
metodo per personalizzare la duplicazione dell'istanza e includere anche la duplicazione della relazione:
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:questo metodo non richiede alcun gem esterno ma richiede la versione più recente di ActiveRecord con #dup
metodo implementato