0

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

  • 模型 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 还包含使用脚手架生成的标准表达式

    <%= 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. 验证助手处理无效的输入值 (< 100) 并传递通知“Id 必须大于或等于 100”并且数据不会更新
  3. :id 的字段被替换为一些正确的值,如 123 并再次提交更新帖子按钮
  4. Rails 不是表单接收和更新数据库,而是在 PostsController#show 中抛出异常 ActiveRecord::RecordNotFound

    找不到 id=42 的帖子

这里发生了什么 ?为什么会出现异常以及为什么再次传递旧值?

4

2 回答 2

1

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

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

你传入的 ID 为 42。因此控制器开始尝试查找 ID 为 42 的 Post 对象。但是,没有 ID 为 42 的 Post 对象,因此会引发错误。它甚至从来没有接到你的update_attributes电话,甚至从来没有去你的验证。

您必须从这个错误中解救出来,或者使用如果没有找到记录find_by_id将返回的方法。nil

另外,我想知道,您为什么要让您的用户手动输入 ID?他们不应该知道这件事。这个 ID 应该与主键不同吗?然后考虑添加一个不同的列,比如identifierorkey或其他东西,以减少这种情况下的歧义。如果你试图操纵 ID 可能会遇到很多问题(实际上想到它可能是 Rails 允许的)。

于 2013-01-04T20:10:22.777 回答
0

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

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

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

于 2013-01-04T20:38:24.240 回答