Какой самый простой способ дублировать запись активной записи?
Я хочу сделать копию записи активной записи, меняя одно поле в процессе (в дополнение к идентификатору). Какой самый простой способ сделать это?
Я понимаю, что могу создать новую запись, а затем перебрать каждое из полей, копируя данные поле за полем - но я подумал, что должен быть более простой способ сделать это...
такие как:
@newrecord=Record.copy(:id) *perhaps?*
12 ответов
Чтобы получить копию, используйте метод clone (или dup для rails 3.1):
# rails < 3.1
new_record = old_record.clone
#rails >= 3.1
new_record = old_record.dup
Затем вы можете изменить любые поля, которые вы хотите.
ActiveRecord переопределяет встроенный клон Object#, чтобы предоставить вам новую (не сохраненную в БД) запись с неназначенным идентификатором.
Обратите внимание, что он не копирует ассоциации, поэтому вам придется делать это вручную, если вам нужно.
Клон Rails 3.1 является мелкой копией, вместо этого используйте dup...
В зависимости от ваших потребностей и стиля программирования, вы также можете использовать комбинацию нового метода класса и слияния. Ввиду отсутствия более простого примера, предположим, что у вас есть задача, запланированная на определенную дату, и вы хотите скопировать ее на другую дату. Фактические атрибуты задачи не важны, поэтому:
old_task = Task.find (task_id) new_task = Task.new (old_task.attributes.merge ({: schedule_on => some_new_date}))
создаст новую задачу с :id => nil
, :scheduled_on => some_new_date
и все остальные атрибуты такие же, как в исходном задании. Используя Task.new, вам придется явно вызывать функцию save, поэтому, если вы хотите, чтобы она сохранялась автоматически, измените Task.new на Task.create.
Мир.
Вам также может понравиться драгоценный камень Amoeba для ActiveRecord 3.2.
В вашем случае, вы, вероятно, хотите использовать nullify
, regex
или же prefix
варианты доступны в конфигурации DSL.
Он поддерживает простое и автоматическое рекурсивное дублирование has_one
, has_many
а также has_and_belongs_to_many
ассоциации, предварительная обработка в полевых условиях и очень гибкая и мощная конфигурация DSL, которая может применяться как к модели, так и на лету.
Обязательно ознакомьтесь с документацией Amoeba, но ее использование довольно просто...
просто
gem install amoeba
или добавить
gem 'amoeba'
в ваш Gemfile
затем добавьте блок амебы к вашей модели и запустите dup
метод как обычно
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
Вы также можете контролировать, какие поля копируются различными способами, но, например, если вы хотите предотвратить дублирование комментариев, но хотите сохранить те же теги, вы можете сделать что-то вроде этого:
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
exclude_field :comments
end
end
Вы также можете предварительно обработать поля, чтобы помочь указать уникальность как с префиксами и суффиксами, так и с регулярными выражениями. Кроме того, есть также множество вариантов, чтобы вы могли писать в наиболее удобочитаемом стиле для ваших целей:
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
Рекурсивное копирование ассоциаций легко, просто включите амебу на дочерних моделях
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
У конфигурации DSL есть еще больше опций, поэтому обязательно ознакомьтесь с документацией.
Наслаждайтесь!:)
Используйте ActiveRecord::Base#dup, если вы не хотите копировать идентификатор
Я обычно просто копирую атрибуты, меняя все, что мне нужно, меняя:
new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
Если вам нужна глубокая копия с ассоциациями, я рекомендую гем deep_cloneable.
В Rails 5 вы можете просто создать дублированный объект или запись, подобную этой.
new_user = old_user.dup
Вот пример переопределения ActiveRecord #dup
метод для настройки дублирования экземпляра и включения дублирования отношений:
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
Примечание: этот метод не требует никакого внешнего драгоценного камня, но он требует более новой версии ActiveRecord с #dup
метод реализован
Самый простой способ:
#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
Или же
# if your rails < 3.1
o = Model.find(id)
(1..109).each do |item|
new_record = o.clone
new_record.save
end
Вы также можете проверить драгоценный камень act_as_inheritable.
"Acts As Inheritable - это Ruby Gem, специально созданный для моделей Rails/ActiveRecord. Он предназначен для использования с само-ссылочной ассоциацией или с моделью, имеющей родителя, который разделяет наследуемые атрибуты. Это позволит вам наследовать любой атрибут или отношение от родительской модели."
Добавляя acts_as_inheritable
к вашим моделям у вас будет доступ к этим методам:
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">
Надеюсь, это поможет вам.
Попробуйте рельсы
dup
метод:
new_record = old_record.dup.save
Поскольку при дублировании модели может быть больше логики, я бы предложил создать новый класс, в котором вы обрабатываете всю необходимую логику. Чтобы облегчить это, есть драгоценный камень, который может помочь: клоун
В соответствии с их примерами документации для модели User:
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
Вы создаете свой класс клонеров:
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
и затем используйте это:
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
Пример скопирован из проекта, но он даст четкое представление о том, чего можно достичь.
Для быстрой и простой записи я бы пошел с:
Model.new(Model.last.attributes.reject {|k,_v| k.to_s == 'id'}