Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails determine if objects from accepts_nested_attributes_for objects changed?

I am aware of the basic dirty indicator methods for rails, which work if direct attributes of an object have changed, I'm wondering how to determine if my children were updated..

I have a form for a collection of files, we'll call it a folder. A folder accepts_nested_attributes_for :files. What I need to determine (within the controller action) is whether or not the files that are within the params hash are different from the ones that are in the db.. So, did the user delete one of the files, did they add a new file, or both (delete one file, and add another)

I need to determine this because I need to redirect the user to a different action if they deleted a file, versus adding a new file, versus just updated attributes of the folder.

like image 837
Rabbott Avatar asked Oct 25 '22 06:10

Rabbott


1 Answers

def update
  @folder = Folder.find(params[:id])
  @folder.attributes = params[:folder]

  add_new_file = false
  delete_file = false
  @folder.files.each do |file|
    add_new_file = true if file.new_record? 
    delete_file = true if file.marked_for_destruction?
  end  

  both = add_new_file && delete_file

  if both
    redirect_to "both_action"
  elsif add_new_file
    redirect_to "add_new_file_action"
  elsif delete_file
    redirect_to "delete_file_action"
  else
    redirect_to "folder_not_changed_action"
  end 
end

Sometimes you want to know that folder is changed without determining how. In that case you can use autosave mode in your association:

class Folder < ActiveRecord::Base 
  has_many :files, :autosave => true
  accepts_nested_attributes_for :files
  attr_accessible :files_attributes
end

Then in controller you can use @folder.changed_for_autosave? which returns whether or not this record has been changed in any way (new_record?, marked_for_destruction?, changed?), including whether any of its nested autosave associations are likewise changed.

Updated.

You can move model specific logic from controller to a method in folder model, e.q. @folder.how_changed?, which can return one of :add_new_file, :delete_file and etc. symbols (I agree with you that it's a better practice, I'd just tried to keep things simple). Then in controller you can keep logic pretty simple.

case @folder.how_changed?
  when :both
    redirect_to "both_action"
  when :add_new_file
    redirect_to "add_new_file_action"
  when :delete_file
    redirect_to "delete_file_action"
  else
    redirect_to "folder_not_changed_action"
end

This solution uses 2 methods: new_record? and marked_for_destruction? on each child model, because Rails in-box method changed_for_autosave? can tell only that children were changed without how. This is just the way how to use this indicators to achieve your goal.

like image 154
Voldy Avatar answered Nov 18 '22 12:11

Voldy