Quelle est la manière la plus simple de dupliquer un enregistrement actif ?
-
09-06-2019 - |
Question
Je souhaite faire une copie d'un enregistrement activerecord, en modifiant un seul champ dans le processus (en plus du identifiant).Quelle est la manière la plus simple d’y parvenir ?
Je me rends compte que je pourrais créer un nouvel enregistrement, puis parcourir chacun des champs en copiant les données champ par champ - mais j'ai pensé qu'il devait y avoir un moyen plus simple de le faire...
tel que:
@newrecord=Record.copy(:id) *perhaps?*
La solution
Pour obtenir une copie, utilisez la méthode clone (ou dup pour Rails 3.1) :
# rails < 3.1
new_record = old_record.clone
#rails >= 3.1
new_record = old_record.dup
Ensuite, vous pouvez modifier les champs de votre choix.
ActiveRecord remplace l'objet#clone intégré pour vous donner un nouvel enregistrement (non enregistré dans la base de données) avec un identifiant non attribué.
Notez qu'il ne copie pas les associations, vous devrez donc le faire manuellement si vous en avez besoin.
Le clone de Rails 3.1 est une copie superficielle, utilisez plutôt dup...
Autres conseils
En fonction de vos besoins et de votre style de programmation, vous pouvez également utiliser une combinaison de la nouvelle méthode de classe et fusionner.Faute de mieux simple Par exemple, supposons que vous ayez une tâche planifiée pour une certaine date et que vous souhaitiez la dupliquer à une autre date.Les attributs réels de la tâche ne sont pas importants, donc :
old_task = Task.find(task_id) new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date}))
créera une nouvelle tâche avec :id => nil
, :scheduled_on => some_new_date
, et tous les autres attributs sont identiques à ceux de la tâche d'origine.En utilisant Task.new, vous devrez appeler explicitement save, donc si vous souhaitez qu'il soit enregistré automatiquement, remplacez Task.new par Task.create.
Paix.
Vous aimerez peut-être aussi le Gemme d'amibe pour ActiveRecord 3.2.
Dans votre cas, vous souhaiterez probablement utiliser le nullify
, regex
ou prefix
options disponibles dans la configuration DSL.
Il prend en charge la duplication récursive simple et automatique de has_one
, has_many
et has_and_belongs_to_many
des associations, un prétraitement sur le terrain et une configuration DSL très flexible et puissante qui peut être appliquée à la fois au modèle et à la volée.
assurez-vous de consulter le Documentation sur l'amibe mais l'utilisation est assez simple...
juste
gem install amoeba
ou ajouter
gem 'amoeba'
à votre Gemfile
puis ajoutez le bloc amibe à votre modèle et exécutez le dup
méthode comme d'habitude
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
Vous pouvez également contrôler quels champs sont copiés de plusieurs manières, mais par exemple, si vous souhaitez empêcher la duplication des commentaires tout en conservant les mêmes balises, vous pouvez faire quelque chose comme ceci :
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
exclude_field :comments
end
end
Vous pouvez également prétraiter les champs pour indiquer le caractère unique avec à la fois des préfixes et des suffixes ainsi que des expressions rationnelles.De plus, il existe également de nombreuses options qui vous permettent d'écrire dans le style le plus lisible correspondant à votre objectif :
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 copie récursive des associations est facile, il suffit d'activer également l'amibe sur les modèles enfants
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 configuration DSL a encore plus d'options, alors assurez-vous de consulter la documentation.
Apprécier!:)
Utiliser ActiveRecord :: Base#dup si vous ne voulez pas copier l'identifiant
En général, je copie simplement les attributs, en modifiant tout ce dont j'ai besoin :
new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
Si vous avez besoin d'une copie complète avec des associations, je recommande le deep_cloneable gemme.
Le moyen le plus simple est le suivant :
#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
Dans Rails 5, vous pouvez simplement créer un objet en double ou un enregistrement comme celui-ci.
new_user = old_user.dup
Vous pouvez également vérifier le actes_as_inheritable gemme.
"Acts As Inheritable est un Ruby Gem spécialement écrit pour les modèles Rails/ActiveRecord.Il est destiné à être utilisé avec le Association autoréférentielle, ou avec un modèle ayant un parent qui partage les attributs héritables.Cela vous permettra d'hériter de n'importe quel attribut ou relation du modèle parent."
En ajoutant acts_as_inheritable
à vos modèles vous aurez accès à ces méthodes :
hériter_attributs
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
hériter_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">
J'espère que cela pourra vous aider.
Puisqu'il pourrait y avoir plus de logique, lors de la duplication d'un modèle, je suggérerais de créer une nouvelle classe, dans laquelle vous gérerez toute la logique nécessaire.Pour faciliter cela, il existe un joyau qui peut vous aider : clown
Selon leurs exemples de documentation, pour un modèle utilisateur :
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
Vous créez votre 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
puis utilisez-le :
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
Exemple copié du projet, mais il donnera une vision claire de ce que vous pouvez réaliser.
Pour un enregistrement simple et rapide, j'irais avec :
Model.new(Model.last.attributes.reject {|k,_v| k.to_s == 'id'}
Voici un exemple de remplacement d'ActiveRecord #dup
méthode pour personnaliser la duplication d'instance et inclure également la duplication de relation :
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
Note:cette méthode ne nécessite aucune gemme externe mais nécessite une version plus récente d'ActiveRecord avec #dup
méthode mise en œuvre