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?*
Était-ce utile?

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top