Why does my rails rollback when I try to user.save?
Ruby on-RailsRubyrailstutorial.orgRuby on-Rails Problem Overview
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: "[email protected]", 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('[email protected]') 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
Ruby on-Rails Solutions
Solution 1 - Ruby on-Rails
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.
Solution 2 - Ruby on-Rails
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.
Solution 3 - Ruby on-Rails
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
Solution 4 - Ruby on-Rails
When save rollbacks, use save! instead, and error log will be printed.
Solution 5 - Ruby on-Rails
Your validations are not passing. You can do:
user.errors.full_messages
in the console after the failed save to see why.
Solution 6 - Ruby on-Rails
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
Solution 7 - Ruby on-Rails
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.
Solution 8 - Ruby on-Rails
Try to update the database:
rails db:migrate