如何防止铁路(3.1)点火2选择相同的记录?

emr*_*ass 6 forms performance ruby-on-rails

我有一个CRUD用户控制器.当我在浏览器中打开"用户编辑"页面时,我的日志显示如下:

Started GET "/users/1/edit" for 127.0.0.1 at 2011-06-21 20:09:37 +0200
  Processing by UsersController#edit as HTML
  Parameters: {"id"=>"1"}
  User Load (0.2ms)  SELECT `users`.* FROM `users` WHERE
   `users`.`id` = ? LIMIT 1  [["id", 1]]
  User Load (0.3ms)  SELECT `users`.* FROM `users` WHERE
   `users`.`id` = ? LIMIT 1  [["id", "1"]]
Run Code Online (Sandbox Code Playgroud)

在编辑操作中,我只是调用一个返回的私有函数用户

@user ||= User.find(params[:id])
Run Code Online (Sandbox Code Playgroud)

该视图如下所示:

<%= settings_title(@user.username) %>
<%= form_for @user, :html => { :multipart => true } do |f| %>
  <%= render "form", :user => @user
  <div class="action"><%= submit_tag t("users.edit.submit"), :class => "button" %></div>
<%= end %>
Run Code Online (Sandbox Code Playgroud)

该路线定义为 resources :users do ...

任何想法如何防止第二次数据库访问将不胜感激!

更新:

似乎可以通过调用来阻止第二个DB SELECT

@user ||= User.find(params[:id].to_i) # notice the .to_i
Run Code Online (Sandbox Code Playgroud)

在编辑动作中.我现在得到:

User Load (0.1ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = ? LIMIT 1  [["id", 1]]
CACHE (0.0ms)      SELECT `users`.* FROM `users` WHERE `users`.`id` = ? LIMIT 1
Run Code Online (Sandbox Code Playgroud)

但这是正确的方法吗?您是否看到此解决方案的任何其他副作用?

Yar*_*boy 3

尽管有 #to_i 解决方法,如果 current_user 是管理员并且可以编辑任何用户记录,那么这似乎是正确的行为。这只是一个巧合,在这种情况下 current_user == user_to_be_edited 并且您获得相同数据的两个数据库命中。在 current_user 正在编辑其他人的用户数据的所有其他情况下,您将必须访问数据库两次。

但是,如果 current_user 只编辑他/她自己的数据,那么在您的控制器中而不是:

@user ||= User.find(params[:id])
Run Code Online (Sandbox Code Playgroud)

你会使用:

@user ||= current_user
Run Code Online (Sandbox Code Playgroud)

...假设用户身份验证在执行操作之前已经发生。通过这种方式,您将只会在身份验证过程中对数据库进行一次命中。

最后一点,在前一种情况下,当前用户管理员可以编辑任何用户,如果您确实想摆脱数据库被击中两次的巧合边缘情况,您可以这样做:

@user ||= current_user.id == params[:id].to_i ? current_user : User.find(params[:id])
Run Code Online (Sandbox Code Playgroud)

通过这种方式,当用户编辑他/她自己的数据时,您将避免额外的数据库命中。