Rails更新表单问题?

时间:2013-01-04 19:51:22

标签: ruby-on-rails ruby-on-rails-3

任何人都可以解释更新表格的以下奇怪行为吗?

  • 模型post.rb包含以下定义

    class Post < ActiveRecord::Base
      attr_accessible :id, :name, :content
      validates :id, :presence => true, :numericality => { :greater_than_or_equal_to => 100}, :uniqueness => true 
    end
    
  • 控制器posts_controller.rb以这种方式定义更新方法

    class PostsController < ApplicationController
      def update
        @post = Post.find(params[:id])
    
        respond_to do |format|
          if @post.update_attributes(params[:post])
            format.html { redirect_to @post, notice: 'Post was successfully updated.' }
            format.json { head :no_content }
          else
            format.html { render action: "edit" }
            format.json { render json: @post.errors, status: :unprocessable_entity }
          end
        end
      end
    
  • 部分视图_form.rb还包含使用scaffold

    生成的标准表达式
    <%= form_for(@post) do |f| %>
      ...
      <div class="field">
        <%= f.label :id %><br />
        <%= f.number_field :id %>
      </div>
      <div class="field">
         <%= f.label :name %><br />
         <%= f.text_field :name %>
      </div>
      <div class="field">
        <%= f.label :content %><br />
        <%= f.text_area :content %>
      </div>
      <div class="actions">
        <%= f.submit %>
      </div>
    <% end %>
    

问题描述:

  1. 在更新表单中的无效值:id为42
  2. 验证助手处理无效输入值(&lt; 100)并传递通知“Id必须大于或等于100”并且数据更新
  3. 字段for:id被替换为一些正确的值,如123,并且“更新帖子”按钮再次提交
  4. 而不是表单接收和更新数据库,Rails抛出异常 PostsController#show

    无法找到ID为42的帖子

  5. 这里发生了什么?为什么说明以及为什么再次传递旧值?

2 个答案:

答案 0 :(得分:1)

问题是你的控制器中你正在这样做:

@post = Post.find(params[:id])

你传入的ID是42.所以控制器去试图找到ID为42的Post对象。但是,没有ID为42的Post,所以它会抛出一个错误。它甚至从未进入update_attributes电话,甚至从未进行验证。

您必须解决此错误,或使用find_by_id方法,如果找不到记录,将返回nil

另外,我想知道,为什么让你的用户手动输入ID?他们不应该知道这一点。这个ID应该与主键不同吗?考虑添加一个不同的列,如identifierkey或其他类似的东西,如果是这样的话,可以减少歧义。如果你试图操纵ID(实际上可以认为它可能被Rails允许),可能会遇到很多问题。

答案 1 :(得分:0)

update_attributes方法不更新主键也不更新只读属性。更重要的是,update_attributes在更新SQL语句中的id条件中使用WHERE属性,因此如果您更改了id属性,您最终将更新已更新的记录这个ID已经存在(或者如果数据库中不存在id,则根本没有更新)。

作为一种解决方法,您要么自己编写更新SQL语句,要么通过创建具有所需id的新记录(复制旧属性并设置新ID)并删除旧记录来两次命中数据库。

那就是说,我同意@MrDanA认为更新主键不是一个好主意,我认为你应该考虑另一种解决问题的方法。