问题
I have a page where a user can see a list of messages in a current threaded conversation.
They can delete each message using a delete link (a link inside an each loop for each message):
<%= link_to 'Delete', message, :method => :delete, :confirm => "Are you sure?", :title => message.body %> <br />
This basically connects them to an action in my messages_controller:
def destroy
@message = Message.find(params[:id])
@message.update_attribute('sender_status', 1) if @message.sender_id == current_user.id
@message.update_attribute('recipient_status', 1) if @message.recipient_id == current_user.id
@message.destroy if @message.sender_status == 1 && @message.recipient_status == 1
flash[:success] = "Message deleted"
redirect_to :back
end
What I'd like to have now is one link before all the messages (so I won't put this in the each block). This link will be called "delete all" and rather than updating 1 attribute with the value 1 it will update all the attributes (sender/recipient_status) to 1 so that a user doesn't have to click on each individual messages delete link.
I'm a bit stuck with how to do this. I don't use checkboxes but rather delete links so for the delete all I'd rather use a link too.
Can any body offer me a solution with an example please? I'd really appreciate it. Thought I'd sleep on it last night and have an idea in the morning but still nothing comes to mind.
Kind regards
Update
The messages belong to a conversation. I can either grab a conversation by: 1) The MessageThread table. This stores the message_id of the first message in the conversation which is the parent_id of the other messages in that conversation. So for example.
+-----+----------+---------+---------+---------+--------+---------+----------+----------+----------+
| id | sende... | reci... | body | pare... | status | crea... | updat... | sende... | recip... |
+-----+----------+---------+---------+---------+--------+---------+----------+----------+----------+
| 220 | 4 | 1 | hi | 220 | 0 | 2012... | 2012-... | 0 | 0 |
| 221 | 1 | 4 | hey | 220 | 0 | 2012... | 2012-... | 0 | 0 |
| 222 | 4 | 1 | hi | 220 | 0 | 2012... | 2012-... | 0 | 0 |
| 223 | 1 | 4 | hi | 220 | 0 | 2012... | 2012-... | 0 | 0 |
| 232 | 1 | 4 | good | 220 | 0 | 2012... | 2012-... | 0 | 0 |
| 233 | 4 | 1 | and ... | 220 | 0 | 2012... | 2012-... | 0 | 0 |
| 237 | 1 | 4 | hmm | 220 | 0 | 2012... | 2012-... | 0 | 0 |
| 238 | 1 | 4 | yay | 220 | 0 | 2012... | 2012-... | 0 | 0 |
| 239 | 1 | 4 | yess | 220 | 0 | 2012... | 2012-... | 0 | 0 |
2) I can also just find the conversation by parent id. e.g. m = Message.find_by_parent_id(220).
to get the other messages in the convo I would just type m.children
Here is how my messagethreads table looks:
+-----+------------+-----------+--------------+--------+---------------------+---------------------+
| id | message_id | sender_id | recipient_id | status | created_at | updated_at |
+-----+------------+-----------+--------------+--------+---------------------+---------------------+
| 102 | 247 | 1 | 5 | 0 | 2012-02-16 16:18... | 2012-02-16 16:18... |
| 101 | 246 | 1 | 3 | 0 | 2012-02-16 16:14... | 2012-02-16 16:14... |
| 90 | 220 | 4 | 1 | 0 | 2012-02-15 14:05... | 2012-02-15 14:05... |
| 89 | 219 | 4 | 38 | 0 | 2012-02-15 12:26... | 2012-02-15 12:26... |
| 88 | 218 | 2 | 4 | 0 | 2012-02-14 13:41... | 2012-02-14 13:41... |
| 87 | 210 | 1 | 2 | 0 | 2012-02-14 13:31... | 2012-02-14 13:31... |
+-----+------------+-----------+--------------+--------+---------------------+---------------------+
Here is what is working for me:
def destroy_all_messages
Message.find_by_parent_id(params[:format]).children.each do |message|
message.update_attribute('sender_status', 1) if message.sender_id == current_user.id
message.update_attribute('recipient_status', 1) if message.recipient_id == current_user.id
message.destroy if message.sender_status == 1 && message.recipient_status == 1
end
flash[:success] = "Messages deleted"
redirect_to :back
end
and:
<%= link_to 'Delete All', messages_delete_all_messages_path(@current_thread.message_id), :method => :delete, :confirm => "Are you sure?" %> <br />
This is what's working for me.. Opinions? I noticed I had to use "params[:format]" to grab the params I passed into the link_to message path in my controller.
回答1:
How are those messages grouped? Do they belong to a Conversation?
If it's a Conversation (with has_many :messages) You could add a "delete all messages" button to the conversation that sends the id of this conversation to a delete_messages action. In this action do
def destroy_messages
Conversation.find(params[:id]).messages.each do |message|
message.update_attribute('sender_status', 1) if message.sender_id == current_user.id
message.update_attribute('recipient_status', 1) if message.recipient_id == current_user.id
message.destroy if message.sender_status == 1 && message.recipient_status == 1
end
end
Could need some details for proper authorization. If there is no Conversation model, do something similar after searching for the messages to delete. (Or tell us some details what exactly you display and how you decide to display it)
Update
This should be just the same. If you can get all messages that easy, just replace "Conversation" with the parent message. You link should send the id of the parent message and then run through all of it's child messages and update their status. Should be easy enough.
m = Message.find(params[:id])
m.children.each do |message|
... as above ...
end
In general, just send an identifier (id of parent in this case) that is good enough to identify the messages to be deleted, use it to run a find and then update the records.
回答2:
Use 'update_attributes' method instead and pass the fields that you want to update as a hash.
回答3:
Thorsten is spot on.
May I suggest an additional clean up?
Message.rb
def is_deletable?(current_user)
if (self.sender_status == 1 || self.sender_id == current_user.id) &&
(self.recipient_status == 1 || self.recipient_id == current_user.id)
true
else
false
end
end
message_controller.rb
def destroy
@message = Message.find(params[:id])
if @message.is_deletable?(current_user)
@message.destroy
flash[:success] = "Message deleted"
else
flash[:error] = "You dont have permission to delete this message"
end
redirect_to :back
end
def destroy_messages
Message.find_by_parent_id(params[:id]).each do |message|
message.destroy if message.is_deletable?(current_user)
end
flash[:success] = "Deleted all permissible messages"
redirect_to :back
end
This has the added benefit of adding an additional update statement to a row you're just about to delete, so should make things faster & more efficient.
来源:https://stackoverflow.com/questions/9327652/updating-more-than-one-attribute-at-one-time-in-ruby-on-rails