Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent rails (3.1) from firing 2 selects for same record?

I have a CRUD users controller. When I open the "user edit" page in the browser, my log shows this:

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"]]

In the edit action, I simply call a private function user, which returns

@user ||= User.find(params[:id])

The view looks as follows:

<%= 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 %>

The route is defined as resources :users do ...

Any idea how to prevent the second db access would be greatly appreciated!

Update:

It seems like the second DB SELECT can be prevented by calling

@user ||= User.find(params[:id].to_i) # notice the .to_i

in the edit action. I now get:

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

but is this the proper way to do it? Do you see any other side-effects of this solution?

like image 510
emrass Avatar asked Jun 21 '11 18:06

emrass


1 Answers

Your #to_i workaround notwithstanding, if current_user is an admin and can edit any user record, then it would seem this is the correct behavior. It's just a coincidence that in this case current_user == user_to_be_edited and you're getting two db hits for the same data. In all the other cases where the current_user is editing someone else's user data, you will have to hit the database twice by necessity.

However, if current_user only ever edits his/her own data, then in your controller instead of:

@user ||= User.find(params[:id])

you would use:

@user ||= current_user

...under the assumption that user authentication has already occurred prior to getting to the action. In this manner, you will only have the one hit on the database that happens in authentication.

As a final note, in the former case, where a current_user admin can edit any user, if you really want to get rid of that one coincidental edge case where the database gets hit twice, you can do this:

@user ||= current_user.id == params[:id].to_i ? current_user : User.find(params[:id])

In this manner, you'll avoid the extra db hit when a user is editing his/her own data.

like image 181
Yardboy Avatar answered Nov 04 '22 13:11

Yardboy