Как проще всего дублировать запись activerecord?
-
09-06-2019 - |
Вопрос
Я хочу сделать копию записи activerecord, изменив при этом одно поле (помимо идентификатор).Каков самый простой способ добиться этого?
Я понимаю, что могу создать новую запись, а затем перебирать каждое из полей, копируя данные поле за полем, но я решил, что должен быть более простой способ сделать это...
такой как:
@newrecord=Record.copy(:id) *perhaps?*
Решение
Чтобы получить копию, используйте метод клонирования (или дублирования для рельсов 3.1):
# rails < 3.1
new_record = old_record.clone
#rails >= 3.1
new_record = old_record.dup
Затем вы можете изменить любые поля, которые хотите.
ActiveRecord переопределяет встроенный Object#clone чтобы предоставить вам новую (не сохраненную в БД) запись с неназначенным идентификатором.
Обратите внимание, что он не копирует ассоциации, поэтому при необходимости вам придется делать это вручную.
Клон Rails 3.1 — это поверхностная копия, вместо этого используйте dup…
Другие советы
В зависимости от ваших потребностей и стиля программирования вы также можете использовать комбинацию нового метода класса и слияния.За неимением лучшего простой Например, предположим, что у вас есть задача, запланированная на определенную дату, и вы хотите продублировать ее на другую дату.Фактические атрибуты задачи не важны, поэтому:
old_task = Task.find(task_id) new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date}))
создаст новую задачу с :id => nil
, :scheduled_on => some_new_date
, а все остальные атрибуты такие же, как у исходной задачи.Используя Task.new, вам придется явно вызывать сохранение, поэтому, если вы хотите, чтобы оно сохранялось автоматически, измените Task.new на Task.create.
Мир.
Вам также может понравиться Драгоценный камень Амеба для ActiveRecord 3.2.
В вашем случае вы, вероятно, захотите использовать nullify
, regex
или prefix
опции доступны в конфигурации DSL.
Он поддерживает простое и автоматическое рекурсивное дублирование has_one
, has_many
и has_and_belongs_to_many
ассоциации, предварительная обработка полей и очень гибкий и мощный конфигурационный DSL, который можно применять как к модели, так и на лету.
обязательно ознакомьтесь с Документация по амебе но пользоваться довольно легко...
только
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 драгоценный камень.
Самый простой способ:
#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
В Rails 5 вы можете просто создать дубликат объекта или сделать запись вот так.
new_user = old_user.dup
Вы также можете проверить 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">
Надеюсь, это поможет вам.
Поскольку логики может быть больше, при дублировании модели я бы предложил создать новый класс, в котором вы обрабатываете всю необходимую логику.Чтобы облегчить это, есть драгоценный камень, который может помочь: клоун
Согласно примерам документации, для модели пользователя:
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'}
Вот пример переопределения 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
метод реализован