Reputation: 11098
I'm using "acts_as_tree" plugin for my user messaging thread feature on my website. I have a method that makes deleting selected messages possible. The messages don't actually get deleted. Their sender_status or recipient_status columns get set to 1 depending on what user is the sender or recipient of the message.
Anyway if both users have those status's set to one then that last line makes sure the message row is completely moved from the database. Now this is fine as long as it's not the parent message being deleted. If the parent message deleted then the children that haven't been selected for deletion won't be accessible anymore.
Here is the method:
def delete_all_users_selected_messages(message_ids, user_id, parent_id)
Message.where(:id => message_ids, :sender_id => user_id).update_all(:sender_status => 1)
Message.where(:id => message_ids, :recipient_id => user_id).update_all(:recipient_status => 1)
Message.delete_all(:sender_status => 1, :recipient_status => 1, :parent_id => parent_id).where("id != ?", parent_id)
end
It's quite obvious what I'm trying to do. I need to have the parent ignored. So where the primary key is equal to the parent_id means that row is a parent (normally the parent_id is nil but I needed it set to the primary keys value for some other reason, long story and not important). Anyway is there an SQL statement I can add on to the end of the last line in tat method? To make sure it only deletes messages where the id of the row is not equal to the parent_id?
I can arrange for the parent_id row to never be permitted for deletion unless the actual thread (MessageThreads table that references the messages tables conversations) is deleted.
Any way how can I make it so this parent row is ignored when that delete_all method is run?
Kind regards
Upvotes: 19
Views: 12641
Reputation: 11098
This worked for me in the end.
Message.where('id != ? AND parent_id = ?', parent_id, parent_id).where(:sender_status => 1, :recipient_status => 1).delete_all
Basically returns all messages of that particular conversation except the one where id == parent_id. Whenever id == parent_id this means it's a parent message.
Upvotes: 2
Reputation: 443
Nowadays in Rails 4, you can do:
Model.where.not(attr: "something").delete_all
and
Model.where.not(attr: "something").destroy_all
And don't forget about difference:
destroy
method. Instantiates all the records and destroys them one at a time, so with a large dataset, this could be slowdestroy
method. Callbacks are not called.Upvotes: 30
Reputation: 96454
I would consider a slightly different approach and have the model that has the has_many
relationship have :dependent => destroy
with it, e.g.
User has_many :messages, :dependent => :destroy
That way you don't get the 'dangling orphan record' issue you describe.
I would try and approach it this way rather than thinking 'all records except'.
I don't know if there is something I am not addressing but this is what comes to find for the issue described.
Upvotes: 1
Reputation: 10874
Why not use association from the parent record, something like this?
Message.where(:id => parent_id).first
.children.where(:sender_status => 1, :recipient_status => 1)
.delete_all
Upvotes: 2