54

I have installed the RailsTutorial sample app(the twitter like application) and am trying to understand why the following piece of console code does not update the database when I try updating the user db. I am expecting the user information to get updated once I use the user.save. However, this rolls back to unedited data. Is this due to a user based restriction?

Users controller:

class UsersController < ApplicationController

#before_filter :signed_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
# By default before filters apply to all actions
#before_filter :correct_user, only: [:edit, :update]
  def edit
    @user = User.find(params[:id])
  end

  def update

    @user = User.find params[:id]

    respond_to do |format|

    if @user.update_attributes(params[:user])
      flash[:notice] = "Profile updated"
      format.html { redirect_to(@user, :notice => 'User was successfully updated.') }
      format.json { respond_with_bip(@user) }
    else

      format.html { render :action => "edit" }
      format.json { respond_with_bip(@user) }
    end
    end
 end

  private


  def correct_user
    @user = User.find(params[:id])
    redirect_to(root_path) unless current_user?(@user)
  end

  def admin_user
    redirect_to(root_path) unless current_user.admin?
  end


end

Rails console:

1.9.3-p392 :001 > user = User.find(109)


User Load (8.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 109]]
 => #<User id: 109, name: "laurie", email: "auriepage@gmail.com", created_at: "2013-09-26 18:10:12", updated_at: "2013-09-26 18:10:12", password_digest: "$2a$10$aXEtun8Z2Deqy2wNxvFRNOjPczKQkYc1vDezP5OduJuF...", remember_token: "YhIUhgFm9iMewxdNOHJ45A", admin: false> 

1.9.3-p392 :002 > user.name = "larry"
 => "larry" 

1.9.3-p392 :003 > user.save
   (0.2ms)  begin transaction
  User Exists (0.6ms)  SELECT 1 AS one FROM "users" WHERE (LOWER("users"."email") = LOWER('auriepage@gmail.com') AND "users"."id" != 109) LIMIT 1
   (0.1ms)  rollback transaction
 => false 

User model:

class User < ActiveRecord::Base

# Declaration of public variables   
  attr_accessible :email, :name, :password, :password_confirmation
  has_secure_password
  has_many :microposts, dependent: :destroy
  has_many :relationships, foreign_key: "follower_id", dependent: :destroy
  has_many :followed_users, through: :relationships, source: :followed
  has_many :reverse_relationships, foreign_key: "followed_id", class_name: "Relationship", dependent: :destroy
  has_many :followers, through: :reverse_relationships, source: :follower

  before_save {email.downcase!}
  before_save :create_remember_token

  validates :name, presence: true, length: {maximum: 50}
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false}
  validates :password, presence: true, length: {minimum: 6}
  validates :password_confirmation, presence: true
  after_validation {self.errors.messages.delete(:password_digest)}

 private
    def create_remember_token
        self.remember_token = SecureRandom.urlsafe_base64
    end
end
Igor Ivancha
  • 3,413
  • 4
  • 30
  • 39
Venomoustoad
  • 1,203
  • 1
  • 14
  • 38
  • 2
    The `.save` method in the console doesn't use the controller code, so posting it is irrelevant. Take a look at your model and try to figure out why that strange `SELECT 1 AS one FROM...` SQL statement is getting issued, and consider posting the code for your model instead of the controller. Also, did you define your own `save` method in your model by chance, or do you have any custom callbacks on that model? Maybe the answer is in there somewhere. – jefflunt Sep 26 '13 at 20:13
  • It could be because User has a uniqueness index on email, and there are more than one user with the email `auriepage@gmail.com`. That would explain the query trying to find a user whose email is that but whose ID is not 109 – Chris Cashwell Sep 26 '13 at 20:14
  • You're having issues with your `User` class, definitely include your `User` class source code. – Nick Veys Sep 26 '13 at 20:15
  • 1
    Also possible that a validation is causing this, include the model in the question. – derekyau Sep 26 '13 at 20:16
  • I have included the user model. I get the following error messages from the validation: 1.9.3-p392 :005 > user.errors.full_messages => ["Password can't be blank", "Password is too short (minimum is 6 characters)", "Password confirmation can't be blank"] These are the validation errors, however, when i am already signed in as the user, I do not want to have to change the password every time i want to change the name. I want the edit to work independent of the password validation (but should be validated that the current user is the one being edited) – Venomoustoad Sep 27 '13 at 03:28

8 Answers8

146

Your user model probably has validations which are not satisfied. Since you've not posted those I'm unable to really solve your question. To make live easier you can debug why your user isn't willing to save.

Try running

user.errors.full_messages

which should give you a hint what's going wrong.

jewilmeer
  • 1,926
  • 1
  • 12
  • 11
  • I get the following error messages from the validation: 1.9.3-p392 :005 > user.errors.full_messages => ["Password can't be blank", "Password is too short (minimum is 6 characters)", "Password confirmation can't be blank"] These are the validation errors, however, when i am already signed in as the user, I do not want to have to change the password every time i want to change the name. I want the edit to work independent of the password validation (but should be validated that the current user is the one being edited) – Venomoustoad Sep 27 '13 at 03:23
  • @Venomoustoad Validations can have conditions, for instance: (code) validates :password, presence: true, length: {minimum: 6}, if: :password_changed? (/code) password_changed should be a method in your user model. You can put the conditions on when you want to validate a password here. – jewilmeer Sep 28 '13 at 22:39
  • 1
    Displaying the error in the console helped with my issue; had to set the user.password before it would let me save. – Sean Perryman Feb 13 '15 at 23:53
  • I also had an issue with my password being to short - but due to the initial error message I looked in the wrong place. Why does it give the `User Exists (0.5ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2 [["email", "mail@mail.com"], ["LIMIT", 1]]` error? – HPage Aug 03 '16 at 05:43
  • Thanks a lot for this. I was banging my head against this issue for 2-3 hours before I thought to even look in this direction, sure enough I was doing something dumb to data validate a boolean field! – mfisch Feb 24 '17 at 15:44
  • And for the newb there it is like adding "render plain: user.errors.full_messages" in your controller – Piccolo May 02 '19 at 17:42
20

I know this is an old post, but hopefully this might help someone going through this tutorial in the future.

As the accepted answer expresses, this is due to validations which are not being satisfied. I ran into this issue as well and found that another workaround is to use the update_attribute method on the user object. For example, if you want to update the name field of a user object and have it automatically save to the database, without having to touch the virtual password and password_confirmation fields, use the following:

user.update_attribute(:name, "larry")

This will update the name field only and save it to the database (no need to call the save method), without having to touch the password and password_confirmation fields.

Richard Gieg
  • 1,276
  • 8
  • 17
  • 3
    Even though the model validation fails, `user.update_attribute` will update the database regardless of the validity as this method skips validation. Also `save(validate: false)` is used if we want to skip the validation and know what we are doing – Sasi_Kala Feb 09 '16 at 17:38
  • This answer is golden. Thanks @Richard Gieg – nuxxxx Dec 12 '17 at 17:13
9

After you try to save or validate an active record model instance you can view more information about what happened with a few useful commands.

user = User.find(108)
user.name = "Larry"
user.valid? # returns false
user.errors.messages # returns something like {email: "Cant be blank"}

Obviously I made that error up because I don't know what your model file looks like but if it roles back its for one of two reasons usually. The first is your validations failed. If their are no error messages its probably because something in your filter chain returned false. For example

class User << ActiveRecord::Base
  before_save :accidentally_return_false

  def accidentally_return_false
    self.some_boolean = (condition == value)
  end
end

user = User.new( params )
user.save # where condition does not equal value
user.valid? # false because of the before save method

Hope that helps

Tyrel Richey
  • 867
  • 8
  • 13
7

When save rollbacks, use save! instead, and error log will be printed.

Max Wong
  • 316
  • 2
  • 9
5

Your validations are not passing. You can do:

user.errors.full_messages

in the console after the failed save to see why.

mechanicalfish
  • 12,696
  • 3
  • 46
  • 41
2

I had this same problem and no errors were being stored. It turned out that my before_save function was returning false which resulted in the save being canceled (I guess? I'm new to rails).

I was trying to set the value of a boolean which could only be set once a file had been uploaded.

Here's what I was doing:

before_save :set_orientation

def set_orientation
  # ...do stuff...
  self[:is_landscape] = ratio > 1  # stores AND returns the boolean value!!
end

The last line in the function was also an implicit return which I did not mean to do. My solution was to make the return explicitly:

before_save :set_orientation

def set_orientation
  # ...do stuff...
  self[:is_landscape] = ratio > 1  # store the boolean value
  return                           # now return
end
Kyle Falconer
  • 8,302
  • 6
  • 48
  • 68
0

I had the same issue of the database rolling back my transactions in the console while I was trying to update the admin property on a user. If you are doing the Hartl rails tutorial, the issue is that if you type in user.errors.messages in the console, it will tell you that the password is too short. That is because in the model there is a password validation before it saves and hashes your password into the password_digest.

The work around to this is in the console perform your normal activity like setting user.admin = true, and then when you are done, enter user.password = "foobar", then enter user.password_confirmation = "foobar", and then when you perform user.save it will commit all your changes.

Michael Klump
  • 141
  • 2
  • 9
0

Try to update the database:

rails db:migrate
dstrants
  • 7,423
  • 2
  • 19
  • 27