Updating more than one attribute at one time in ruby on rails

两盒软妹~` 提交于 2019-12-13 07:15:33

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!