Back-end: I have a model (User) that has_many of another model (ContactPreference).
Front-end: An interface allowing the user to reorder, add, and delete contact preferences for a particular user.
I’d like to let the user commit all their changes all at once with a single form submit. The way I’m doing this now is with allows_nested_attributes_for :contact_preferences in the User model, and naively POSTing the attributes of the edited preferences list. It works just fine except for a glaring bug: If a user deletes a contact preference, the ID simply isn’t sent, and the preference doesn’t get deleted from the DB.
allows_nested_attributes_for has support for deleting objects from the collection, but it requires the client to keep track of what IDs were deleted and pass a '_destroy' => 1 parameter. This is messy logic that I’d rather avoid; I just want objects deleted unless they are explicitly included in the parameters. allows_nested_attributes_for doesn’t support this behavior as far as I can tell, so I’m looking to implement my own solution.
What’s the most efficient (in terms of database access) way to do this kind of update? Do I delete everything and rebuild the list from scratch? Do I load the association and pick out objects that aren’t explicitly included? Perhaps there’s some clever ActiveRecord magic I can use?
My personal feeling is that doing this using the
:destroy => 1flag set a lot less messy than the alternative. The alternative would be loading the association on the server, comparing the incoming parameters, figuring out which records are missing, then deleting the missing ones and updating the remaining ones. That’s a lot of extra logic, DB operations, and worst of all, you’ll have to hand-rework theaccepts_nested_attributes_forwhich is a non-trivial feat.HTML give you a little trick/hack to accomplish this without JS. Add a checkbox to each record with name
:destroy. Use the high-level form helpers, e.g.check_box, notcheck_box_tag(which requires a lot of things to get right manually), or a higher level form helper such as the simple_form gem.If the flag is not checked, then HTML won’t submit anything, and the record stays. If the flag is checked, HTML will submit the
:destroyflag, and it will be deleted with the built-in server-side mechanisms out of the box.You didn’t say much about your front-end code; it sounds like you have a bunch of JS on there. You probably hide the record when the user “removes” it, you can simply add the
destroyflag programmatically in that case, if you don’t want to use the check box method above. This will be a lot simpler and less error prone than trying to second-guess the backend behavior.