Jak najłatwiej powielić rekord activerecord?

Chcę zrobić kopię rekordu activerecord, zmieniając Pojedyncze pole w procesie (oprócz id ). Jaki jest najprostszy sposób, aby to osiągnąć?

Zdaję sobie sprawę, że mogę utworzyć nowy rekord, a następnie iterację nad każdym z pól kopiując dane pole po polu-ale pomyślałem, że musi być łatwiejszy sposób, aby to zrobić...

Takie jak:

 @newrecord=Record.copy(:id)  *perhaps?*
Author: tereško, 2008-09-12

11 answers

Aby uzyskać kopię, użyj metody clone (lub dup dla rails 3.1+):

# rails < 3.1
new_record = old_record.clone

#rails >= 3.1
new_record = old_record.dup

Następnie możesz zmienić dowolne pola.

ActiveRecord nadpisuje wbudowany obiekt # clone , aby dać Ci nowy (nie zapisany do DB) rekord z niepodpisanym identyfikatorem.
Zauważ, że nie kopiuje skojarzeń, więc w razie potrzeby musisz to zrobić ręcznie.

Rails 3.1 clone jest płytką kopią, zamiast tego użyj dup...

 635
Author: Michael Sepcot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-29 19:38:56

W zależności od twoich potrzeb i stylu programowania, możesz również użyć kombinacji nowej metody klasy i merge. Z powodu braku lepszego prostego przykładu Załóżmy, że masz zadanie zaplanowane na określoną datę i chcesz je powielić na inną datę. Rzeczywiste atrybuty zadania nie są ważne, więc:

old_task = Task.find(task_id)
new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date}))

Utworzy nowe zadanie z :id => nil, :scheduled_on => some_new_date, i wszystkie inne atrybuty takie same jak oryginalne zadanie. Korzystanie Z Zadania.Nowy, trzeba będzie wyraźnie zadzwonić Zapisz, więc jeśli chcesz go zapisać automatycznie, Zmień zadanie.nowy w zadaniu.twórz.

Pokój.
 75
Author: Phillip Koebbe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-15 13:48:34

Możesz również polubić amoeba gem dla ActiveRecord 3.2.

W Twoim przypadku, prawdopodobnie chcesz skorzystać z nullify, regex lub prefix opcje dostępne w konfiguracji DSL.

Obsługuje łatwe i automatyczne powtarzanie rekurencyjne has_one, has_many i has_and_belongs_to_many Asocjacje, wstępne przetwarzanie pola oraz wysoce elastyczna i wydajna konfiguracja DSL, która może być stosowana zarówno do modelu, jak i w locie.

Koniecznie sprawdź Amoeba Dokumentacja ale korzystanie z niej jest całkiem proste...

Po prostu

gem install amoeba

Lub dodać

gem 'amoeba'

Do Twojego Gemfile

Następnie Dodaj blok ameba do modelu i uruchom metodę dup jak zwykle

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

Możesz również kontrolować, które pola są kopiowane na wiele sposobów, ale na przykład, jeśli chcesz zapobiec powielaniu komentarzy, ale chcesz zachować te same tagi, możesz zrobić coś takiego:

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    exclude_field :comments
  end
end

Możesz również wstępnie przetworzyć pola pomagające wskazywać unikalność zarówno za pomocą prefiksów i przyrostków, jak i wyrażeń regularnych. Ponadto, istnieje również wiele opcji, dzięki czemu można pisać w najbardziej czytelnym stylu dla swojego celu:

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

Rekurencyjne kopiowanie skojarzeń jest łatwe, wystarczy włączyć ameba również na modelach potomnych

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

Konfiguracja DSL ma jeszcze więcej opcji, więc koniecznie zapoznaj się z dokumentacją.

Smacznego! :)
 34
Author: Vaughn Draughon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-28 16:27:38

Użyj ActiveRecord:: Base#dup jeśli nie chcesz kopiować id

 31
Author: bradgonesurfing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-18 16:58:40

Zwykle kopiuję atrybuty, zmieniając to, co trzeba zmienić:

new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
 25
Author: François Beausoleil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-29 20:35:58

Jeśli potrzebujesz głębokiej kopii ze skojarzeniami, polecam gem deep_cloneable.

 10
Author: raidfive,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-01 02:22:49

W Rails 5 możesz po prostu utworzyć duplikat obiektu lub rekord w ten sposób.

new_user = old_user.dup
 6
Author: Foram Thakral,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-04 08:56:28

Oto przykład nadpisującej metody ActiveRecord #dup do dostosowywania duplikacji instancji i dołączania duplikacji relacji:

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

Uwaga: ta metoda nie wymaga żadnego zewnętrznego kleju, ale wymaga nowszej wersji ActiveRecord z zaimplementowaną metodą #dup

 3
Author: Zoran Majstorovic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-20 07:04:13

Prosty sposób to:

#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

Lub

# if your rails < 3.1
 o = Model.find(id)
 (1..109).each do |item|
   new_record = o.clone
   new_record.save
 end     
 2
Author: ThienSuBS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-30 13:58:00

Możesz również sprawdzićacts_as_inheritable gem.

"Acts As Inheritable to klejnot Ruby napisany specjalnie dla modeli Rails/ActiveRecord. Jest on przeznaczony do użycia z asocjacją Self-Referential, lub z modelem posiadającym rodzica, który posiada dziedziczone atrybuty. Pozwoli to na dziedziczenie dowolnego atrybutu lub relacji z modelu nadrzędnego."

Dodając acts_as_inheritable do swoich Modeli będziesz miał dostęp do tych metody:

Inherit_attributes

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

Inherit_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">
Mam nadzieję, że to ci pomoże.
 0
Author: esbanarango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-27 22:27:24

Ponieważ może być więcej logiki, podczas powielania modelu, sugerowałbym utworzenie nowej klasy, w której będziesz obsługiwał całą potrzebną logikę. Aby to ułatwić, istnieje klejnot, który może pomóc: clowne

Zgodnie z przykładami dokumentacji, dla modelu Użytkownika:

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

Tworzysz klasę klonera:

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

A następnie użyj go:

user = User.last
#=> <#User(login: 'clown', email: '[email protected]')>

cloned = UserCloner.call(user, email: '[email protected]')
cloned.persisted?
# => false

cloned.save!
cloned.login
# => nil
cloned.email
# => "[email protected]"

# associations:
cloned.posts.count == user.posts.count
# => true
cloned.profile.name
# => nil

Przykład skopiowany z projektu, ale da jasną wizję tego, co można osiągnąć.

Na szybki i prosty zapis wybrałbym:

Model.new(Model.last.attributes.reject {|k,_v| k.to_s == 'id'}

 0
Author: Paulo Fidalgo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-16 14:28:51