update_attribute使用ruby中的where子句

时间:2014-04-23 13:26:18

标签: ruby-on-rails

在我的通讯表中,我有一些专栏:

id | UserID | CommunicationMode | CommunicationDetail | Private
1  | 1      | Phone             | 123456789           | 1
2  | 1      | Email             | abc@abc.com         | 1

我想使用where子句使用如下所示的循环来更新列值:

create
    @user_communication=Communication.where(:UserID => current_user.id)
    if !@user_communication.blank?
      @user_communication.each do |c|
        if params[:ChkBx_Phone].to_i == 1
          c.where("CommunicationMode == 'Phone'").update_attribute( :Private, "1")
        elsif params[:ChkBx_Phone].to_i == 0
          c.where("CommunicationMode == 'Phone'").update_attribute( :Private, "0")
        end
        if params[:ChkBx_Email].to_i == 1
          c.where("CommuicationMode == 'Email'").update_attribute( :Private, "1")
        elsif params[:ChkBx_Email].to_i == 0
          c.where("CommunicationMode == 'Email'").update_attribute( :Private, "0")
        end
      end  
    end
end

我想检查一下,如果选中Phone复选框,则会更新Private列,其中值为1,其他0CommunicationMode is Phone,而对于电子邮件我如果选中Email复选框,则会检查是否会更新Private列,其值为1,其他0其中CommunicationMode is Email

以下是PhoneEmail复选框:

    <table>
    <% @user_communication.each do |c| %>
           <tr>
               <td>
                  <% if c.CommunicationMode == "Phone" and c.Private.to_s == "1" %>
                            <input type="checkbox" name="ChkBx_Phone"
                        id="ChkBx_Phone" value="1" checked = "checked">
                            <%= label(:lb_Phone, "Phone") %>
                  <% elsif c.CommunicationMode == "Phone" and c.Private.to_s == "0" %>
                                <%= check_box_tag 'ChkBx_Phone' %>
                                <%= label(:lb_Phone, "Phone") %>
                  <% end %>
               </td>
           </tr>
           <tr>
               <td>
                   <% if c.CommunicationMode == "Email" and c.Private.to_s == "1" %>
                        <input type="checkbox" name="ChkBx_Email"
                        id="ChkBx_Email" value="1" checked = "checked">
                        <%= label(:lb_Email, "Email") %>
                   <% elsif c.CommunicationMode == "Email" and c.Private.to_s == "0" %>
                        <%= check_box_tag 'ChkBx_Email' %>
                        <%= label(:lb_Email, "Email") %>                    
                   <% end %>
              </td>
           </tr>
    <% end %>
</table>

但我收到的错误如下:

undefined method `where' for #<Communication:0x4bc5490>

但是当我使用下面的代码时:

create
    @user_communication=Communication.where(:UserID => current_user.id)
    if !@user_communication.blank?
      @user_communication.each do |c|
        if params[:ChkBx_Phone].to_i == 1
          puts "Hassan2"
          c.update_attribute( :Private, "1")
        elsif params[:ChkBx_Phone].to_i == 0
          puts "Ali2"
          c.update_attribute( :Private, "0")
        end
      end  
    end
end

它的工作正常,但它同时更新了Private column PhoneEmail的{​​{1}}值,我只检查了Phone复选框。 请建议我,等待你的回复。 感谢。

2 个答案:

答案 0 :(得分:0)

在您的代码中

 @user_communication.each do |c|    
 # Your logic
 end

您循环@user_communication然后c变量将包含Communication object而不是Active record relation,那么您正在执行c.where(),c包含Communication object。但是 .where Active record relation。所以它会抛出错误。

答案 1 :(得分:0)

正如金斯顿所说,当你遍历@user_communication集合时,块中的c变量实际上是一个具体的Communication对象,而不是ActiveRecord::Relation对象,这是包含.where查询方法的内容。

此外,我注意到还有其他一些问题。一些,我不能直接帮你解决,因为我不知道你的系统是如何工作的,但我会指出它们并尝试建议一个替代方案,希望你能找到解决它们的最佳方法。你的系统要求。

关于create方法,有两种方法具有相同的最终结果,但其中一种我会考虑“天真的方法”。我将展示这种天真的方法,只是为了给你一个直接替代/回答你发布的第一个创建方法,所以你可以看到差异并找出最佳解决方案:

天真的方法

def create
  @user_communication=Communication.where(:UserID => current_user.id)

  # Transaction so we don't execute X number of individual update statements
  Communication.transaction do 

    @user_communication.each do |c|

      if c.CommunicationMode == "Phone"
        # Note: Its not necessary to use the ternary operator if you're not 
        # comfortable with it, however it can save you a few lines of code.
        c.update(Private: (params[:ChkBx_Phone].to_i == 1 ? "1" : "0") )
      elsif c.CommunicationMode == "Email"
        c.update(Private: (params[:ChkBx_Email].to_i == 1 ? "1" : "0") )
      end

    end # end communication loop

  end # end transaction
end

这将遍历该用户ID的Communication个对象,并根据其CommunicationMode值更新每个对象,将其设置为1或0,具体取决于两者的值复选框。您将在日志中看到的内容是集合中每个UPDATE对象的几个Communication语句。通常,数据库会立即执行UPDATE语句,因为更新包含在它们自己的事务中,因此,如果您有大量记录,这会随着时间的推移而变得相当昂贵。因此,为了避免这个问题,你可以看到我已经将整个操作包装在一个事务中,因此,更新只在最后提交;这样效率更高(您可以自己进行一些实验来验证这一点;无论是否使用事务包装器,您都应该能够看到明显的时间差异。)

现在,“潜在”更好的方法:

“可能”更好/更不天真的方法

... other method code
Communication.where(UserID: current_user.id, CommunicationMode: "Phone").update_all(Private: (params[:ChkBx_Phone].to_i == 1 ? "1" : "0") )

Communication.where(UserID: current_user.id, CommunicationMode: "Email").update_all(Private: (params[:ChkBx_Email].to_i == 1 ? "1" : "0") )

这将只执行两个UPDATE语句(如果您愿意,也可以在事务中包装)。这应该比上述方法更快地执行,并为您节省更多的代码。

请注意,这个和天真的方法都可以移动到Communication模型中的方法,以便将与模型相关的大量操作保留在控制器之外。

另一个注意事项

在您的视图代码中,您似乎是对集合@user_communication进行迭代,您在其中为每个对象创建一个复选框:

<input type="checkbox" name="ChkBx_Email" id="ChkBx_Email" value="1" checked = "checked">

因此,如果该集合中有五个对象,您将看到一个包含五个复选框的列表。由于您命名此输入的方式,只有一个值被发送(我相信它通常是DOM中的“最后”输入)。换句话说,如果您点击第一个复选框“on”,但保留最后一个“off”,则复选框的值将为“off”。我有一种强烈的感觉,这可能不是您希望系统的行为方式。也许您会希望将这些复选框放在循环之外(上图),因为它们似乎与Communication对象本身没有任何关系。

此外,永远不会向服务器发送未选中的复选框。如果您取消选中该复选框,则params[:ChkBx_Phone].to_i == 1行将会崩溃,因为params[:ChkBx_Phone]为零,并且您将在nil对象上调用to_iThis answer向您展示了此解决方案的替代方案。在您的情况下,您需要确保Rails帮助程序标记,例如check_box_taghidden_field_tag,如下所示:

<%=hidden_field_tag 'ChkBx_Phone', '0'%>
<%=check_box_tag 'ChkBx_Phone', '1', true %>

因此,如果取消选中该复选框,由于存在同名隐藏字段输入,服务器将始终接收此参数的值。