In memory delete from an rails Association - a counterpart to build 4
I don’t like how a lot of the association methods automatically go through to the DB. I like to be able to work with my objects in memory more, get them actually how I want, and then save to the DB. There are times that having instant DB saving go through can really mess with a object that is live on a site. For creation there is such a method: build. And for editing: an update won’t go through until you save it. There isn’t an obvious counterpart for deletion.
I found one by googling around for something else, and noticing people complaining about delete_if not working as they expected. delete_if does exactly what I want, operate on the collection without effecting the DB. Of course, this doesn’t do much good without a way to save it when you are ready. The easiest way I’ve come up with so far is to keep a copy of the initial ids array, and then compare to that later.
class Parent < ActiveRecord::Base
has_many :children
attr_accessor :initial_children_ids
def commit_delete_if_children
parent.connection.delete <<-SQL, "Delete_ifing children"
DELETE FROM children WHERE id IN ('#{(initial_children_ids - children_ids).join('\',\'')}')
SQL
end
protected
def after_initialize
self.initial_children_ids = self.children_ids
end
end
>> dad.children.delete_if { |child| child.id == 3}
>> dad.commit_delete_if_children
=> 1
>> dad.commit_delete_if_children
=> 0

Articles via rss or email
Ah…
.delete_ifis a method onEnumerable, maybe, and not overridden byAssociationProxylike regular.delete. That would explain why you can abuse it in such a way. You can probably use.target.deleteto get the real.delete. When in doubt,Marshal.dumpthe object and look at the first 100 chars to see the class name. The.classmethod lies—it too is proxied.Ya, I had checked out the core mailing list and it didn’t sounds like they were adding delete_if overriding anytime soon, so I figured it was a reasonably safe hack.
Thanks for filling in more details, evan.
I don’t know if you saw my particular post about
delete_if, but I am one of the people annoyed about it not working as I expect. I mean, I’ve come to see it as doing the same thingdeletedoes with some logic attached to it, but Rails changes that.I often do things that work very well with ORM, but as you said, it can be problematic. When that’s the case, you obviously want to get everything ready, to stage the new version somewhere (like memory), and then push it all at once. I just don’t know if
commit_delete_if_childrenis the way to do it.Ya, I saw your post. As you can gather, I’m now glad of the extra flexibility.
It’s not the most elegant way it could be done, no. It’s just the one that doesn’t require reworking a bunch of ActiveRecord code, which is some of the more complicated code in Rails. Also, you wouldn’t want to use this on an overly large set of records, obviously. Ideally you wouldn’t need this sort of manual callback and AR would just handle it all exactly like you’d want, but unfortunately “Do what I mean” is one of the harder magic functions to write, in any language.
I actually had already reworked my domain model a bit so this technique wasn’t required, by the time I figured it out.