Как проще всего продублировать запись activerecord?

Я хочу сделать копию записи activerecord, изменив в процессе одно поле (в дополнение к я бы). Как это сделать проще всего?

Я понимаю, что могу создать новую запись, а затем перебирать каждое из полей, копируя данные поле за полем, но я подумал, что должен быть более простой способ сделать это ...

Такие как:

 @newrecord=Record.copy(:id)  *perhaps?*
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
427
0
195 929
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Ответ принят как подходящий

Чтобы получить копию, используйте метод clone (или dup для rails 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 ...

Это все еще работает в Rails 3.1.0.beta? Когда я использую q = p.clone, а затем p == q, я получаю обратно true. С другой стороны, если я использую q = p.dup, я получаю обратно false при их сравнении.

Juno Woods 08.02.2011 05:10

Документы Rails 3.1 о клоне говорит, что он все еще работает, но я использую Rails 3.1.0.rc4, и даже метод new? не работает.

Turadg 23.06.2011 02:54

Когда я пытаюсь клонировать и затем сохранять в Rails 3.1 RC4, я получаю ошибку Mysql2::Error: Duplicate entry на первичном ключе.

skattyadz 23.06.2011 16:55

Похоже, эта функция была заменена на dup: gist.github.com/994614

skattyadz 23.06.2011 17:01

Однозначно НЕ используйте клон. Как упоминалось в других плакатах, метод clone теперь делегирует использование Kernel # clone, которое будет копировать идентификатор. С этого момента используйте ActiveRecord :: Base # dup

bradgonesurfing 05.08.2011 17:57

Делает ли dup то же самое, что и pre-rails 3.1? Или его функционал другой?

stringo0 25.10.2011 19:27

Должен сказать, это была настоящая боль. Подобное простое изменение предполагаемой функциональности могло бы парализовать некоторые важные функции, если бы у вас не было хорошего охвата спецификаций.

Matt Smith 19.03.2012 23:38

Дополнением для dup или clone, если вы хотите изменить определенные атрибуты, является использование tap, например. clone = record.dup.tap { |new_clone| new_clone.name = "dup_#{new_clone.name}" }

Pablo Cantero 30.06.2013 20:38

Мой тест показывает, что myrecord.clone дает мне точную копию записи, включая ее идентификатор. так что myrecord.save Я вернулся туда, откуда начал. myrecord.dup дает мне и точную копию, но идентификатор равен нулю. Теперь myrecord.save означает, что у меня есть дополнительная копия. Он скопировал все атрибуты, включая мои двоичные данные. Так что используйте ответ Луиса

bobbdelsol 08.07.2014 02:13

После этого вы должны new_record.save! сохранить объект.

Fabio Ros 28.09.2017 18:56

Обычно я просто копирую атрибуты, меняя все, что мне нужно изменить:

new_user = User.new(old_user.attributes.merge(:login => "newlogin"))

Когда я это делаю, я получаю ошибку unknown attribute с одним столбцом из-за столбца, который существует из-за отношения has_many. Есть ли способ обойти это?

Ruben Martinez Jr. 05.07.2014 23:55

с rails4 он не создает уникальный идентификатор для записи

Ben 14.03.2015 16:48

Чтобы создать новую запись с помощью Rails 4, выполните User.create(old_user.attributes.merge({ login: "newlogin", id: nil })). Это сохранит нового пользователя с правильным уникальным идентификатором.

RajeshM 16.03.2015 01:03

В Rails есть Хэш # кроме и Хеш-сегмент, что потенциально делает предлагаемый метод наиболее мощным и менее подверженным ошибкам. Не нужно добавлять дополнительные библиотеки, их легко расширить.

kucaahbe 14.06.2018 17:11

В зависимости от ваших потребностей и стиля программирования вы также можете использовать комбинацию нового метода класса и слияния. Из-за отсутствия лучшего примера просто предположим, что у вас есть задача, запланированная на определенную дату, и вы хотите продублировать ее на другую дату. Фактические атрибуты задачи не важны, поэтому:

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, вам нужно будет явно вызвать save, поэтому, если вы хотите, чтобы оно сохранялось автоматически, измените Task.new на Task.create.

Мир.

Не совсем уверен, насколько хороша идея, что вам вернули WARNING: Can't mass-assign protected attributes: id, due_date, created_at, updated_at

bcackerman 07.11.2012 10:39

Когда я это делаю, я получаю ошибку неизвестного атрибута с одним столбцом из-за столбца, который существует из-за отношения has_many. Есть ли способ обойти это?

Ruben Martinez Jr. 05.07.2014 23:57

@RubenMartineJr. Я знаю, что это старый пост, но да, вы можете обойти это, используя '.except' в хэше атрибутов: new_task = Task.new (old_task.attributes.except (: attribute_you_dont_want‌,: another_aydw) .merge ({: schedule_on => some_new_date}))

Ninigi 30.01.2015 18:58

@PhillipKoebbe, спасибо, но что, если я хочу, чтобы идентификатор не был нулевым? Я хочу, чтобы рельсы автоматически назначали новый идентификатор при создании дубликата - возможно ли это?

BKSpurgeon 16.04.2016 05:04

old task.attributes, к сожалению, также назначает поле идентификатора. У меня не работает

BKSpurgeon 16.04.2016 05:33

Используйте ActiveRecord :: Base # dup, если вы не хотите копировать идентификатор

@Thorin в соответствии с принятым ответом выше, похоже, что правильный метод для Rails <3.1 - .clone

Dan Weaver 10.04.2017 22:23

Если вам нужна глубокая копия с ассоциациями, я рекомендую гем deep_cloneable.

Я тоже. Я попробовал этот драгоценный камень, и он сработал с первого раза, очень прост в использовании.

Rob 13.06.2012 01:26

Вам также может понравиться Драгоценный камень амебы для 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 имеет еще больше опций, поэтому обязательно ознакомьтесь с документацией.

Наслаждаться! :)

Отличный ответ. Спасибо за подробности!

Derek Prior 15.03.2012 04:49

Спасибо, это работает!! Но у меня есть один вопрос, как мне добавить новые записи с клонированием перед сохранением клонированного объекта?

Mohd Anas 08.10.2013 09:44

Просто исправление здесь. Правильный метод - .amoeba_dup, а не только .dup. Я пытался выполнить этот код, но здесь он не работал.

Victor 13.08.2018 23:05

Вы также можете проверить гем act_as_inheritable.

«Acts As Inheritable - это драгоценный камень Ruby, специально написанный для моделей 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">

Надеюсь, это поможет тебе.

Самый простой способ:

#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     

Поскольку при дублировании модели может быть больше логики, я бы предложил создать новый класс, в котором вы будете обрабатывать всю необходимую логику. Чтобы облегчить это, есть драгоценный камень, который может помочь: клоун

Согласно их примерам документации, для модели 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: '[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

Пример скопирован из проекта, но он даст четкое представление о том, чего вы можете достичь.

Для быстрой и простой записи я бы выбрал:

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.

В Rails 5 вы можете просто создать дубликат объекта или такой записи.

new_user = old_user.dup

Другие вопросы по теме