Клонирование записи в рельсах, возможно ли клонировать ассоциации и глубокое копирование? - PullRequest
30 голосов
/ 12 мая 2011

Я. Клонирую запись в рельсах ...

  new_blerg = Blerg.find(1).clone

Эта запись имеет множество и множество ассоциаций, и эти ассоциации даже имеют ассоциации.

Есть лиспособ глубокого копирования и клонирования записи, чтобы она также была клонирована со всеми этими ассоциациями?

Ответы [ 3 ]

29 голосов
/ 28 февраля 2012

Вы можете получить хорошее использование от Amoeba gem для ActiveRecord 3.2.

Он поддерживает простое и автоматическое рекурсивное дублирование ассоциаций has_one, has_many и has_and_belongs_to_many, предварительную обработку поля и очень гибкий и мощный конфигурационный DSL, который может применяться как к модели, так и на лету.

Обязательно ознакомьтесь с Документацией Amoeba , но использовать ее довольно просто ...

только

gem install amoeba

или добавить

gem 'amoeba'

в ваш Gemfile

затем добавьте блок amoeba в вашу модель и запустите метод 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

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

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

  amoeba do
    include_field :comments
  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
    recognize :has_and_belongs_to_many
  end
end

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

Amoeba также автоматически вернется к дочерним записям, если вы включите их

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

Вы также можете добавить в поля некоторые дополнительные данные для указания уникальности

class Post < ActiveRecord::Base
  has_many :comments

  amoeba do
    enable
    prepend :title => "Copy of "
  end
end

и в дополнение к prepend вы также можете добавить или запустить регулярное выражение для данного поля

Наслаждайтесь! :)

20 голосов
/ 12 мая 2011

Вам нужно написать свой собственный метод clone_with_associations, который проходит через определенный список ассоциаций.Теоретически, вы могли бы написать что-то общее, использующее отражение_on_all_associations, но вам нужно будет сделать то же самое для связанных объектов, и это неизбежно приведет к созданию цикла, генерирующего бесконечное количество записей.

Так что, просто напишите свое.Что-то вроде

  #in Blerg
  has_many :foos
  has_many :bars #bars also have many chickens which we want to copy over as well
  def clone_with_associations
    new_blerg = self.dup
    new_blerg.save
    #simple association
    new_blerg.foos = self.foos
    #two-level association 
    self.bars.each do |bar|
      new_bar = bar.clone
      new_bar.save
      new_bar.chickens = bar.chickens 
      new_blerg.bars << bar
    end
    new_blerg
  end

Теперь вы можете сделать

@new_blerg = Blerg.find(1).clone_with_associations
16 голосов
/ 13 июня 2012

В равной степени, этот камень, кажется, хорошо работает: https://github.com/moiristo/deep_cloneable, и довольно прост в использовании.

Просто

gem ‘deep_cloneable’, ‘~> 1.4.0’

, а затем:

pirate.deep_clone :include => :mateys

...