'Why does Rails rollback even though the model exists?

I am trying to find out why my update method is not working in my Rails API. It should update the bio field. I have my API hosted on Heroku and am using the Heroku logs to debug in production. I used the exists? method to make sure the user is in the db and yet when the update method is called it rollsback after doing this check. I don't understand what is the cause of this?

Here are the Heroku logs of the output

2022-04-15T02:54:34.083586+00:00 app[web.1]: I, [2022-04-15T02:54:34.083515 #4]  INFO -- : [9e5ea776-5b15-420f-8cdc-0601480e0e3d] Started PATCH "/users/8" for 98.248.0.125 at 2022-04-15 02:54:34 +0000
2022-04-15T02:54:34.084345+00:00 app[web.1]: I, [2022-04-15T02:54:34.084290 #4]  INFO -- : [9e5ea776-5b15-420f-8cdc-0601480e0e3d] Processing by UsersController#update as HTML
2022-04-15T02:54:34.084376+00:00 app[web.1]: I, [2022-04-15T02:54:34.084350 #4]  INFO -- : [9e5ea776-5b15-420f-8cdc-0601480e0e3d]   Parameters: {"bio"=>"test", "id"=>"8", "user"=>{"bio"=>"test"}}
2022-04-15T02:54:34.087450+00:00 app[web.1]: D, [2022-04-15T02:54:34.087403 #4] DEBUG -- : [9e5ea776-5b15-420f-8cdc-0601480e0e3d]   User Load (1.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 8], ["LIMIT", 1]]
2022-04-15T02:54:34.089711+00:00 app[web.1]: D, [2022-04-15T02:54:34.089664 #4] DEBUG -- : [9e5ea776-5b15-420f-8cdc-0601480e0e3d]   User Exists? (1.2ms)  SELECT 1 AS one FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 8], ["LIMIT", 1]]
2022-04-15T02:54:34.092004+00:00 app[web.1]: D, [2022-04-15T02:54:34.091963 #4] DEBUG -- : [9e5ea776-5b15-420f-8cdc-0601480e0e3d]   TRANSACTION (0.9ms)  BEGIN
2022-04-15T02:54:34.093523+00:00 app[web.1]: D, [2022-04-15T02:54:34.093465 #4] DEBUG -- : [9e5ea776-5b15-420f-8cdc-0601480e0e3d]   User Exists? (1.4ms)  SELECT 1 AS one FROM "users" WHERE "users"."username" = $1 AND "users"."id" != $2 LIMIT $3  [["username", "newperson"], ["id", 8], ["LIMIT", 1]]
2022-04-15T02:54:34.095530+00:00 app[web.1]: D, [2022-04-15T02:54:34.095493 #4] DEBUG -- : [9e5ea776-5b15-420f-8cdc-0601480e0e3d]   TRANSACTION (0.9ms)  ROLLBACK
2022-04-15T02:54:34.096881+00:00 app[web.1]: I, [2022-04-15T02:54:34.096842 #4]  INFO -- : [9e5ea776-5b15-420f-8cdc-0601480e0e3d] [active_model_serializers] Rendered ActiveModel::Serializer::Null with Hash (0.07ms)
2022-04-15T02:54:34.097078+00:00 app[web.1]: I, [2022-04-15T02:54:34.097050 #4]  INFO -- : [9e5ea776-5b15-420f-8cdc-0601480e0e3d] Completed 422 Unprocessable Entity in 13ms (Views: 0.6ms | ActiveRecord: 5.5ms | Allocations: 2816)
2022-04-15T02:54:34.101664+00:00 heroku[router]: at=info method=PATCH path="/users/8" host=anime-axis-api.herokuapp.com request_id=9e5ea776-5b15-420f-8cdc-0601480e0e3d fwd="98.248.0.125" dyno=web.1 connect=0ms service=17ms status=422 bytes=1096 protocol=https

Here is my update method:

def update
  if User.exists?(8)
    @current_user.update!(user_params)
    render json: @current_user, status: :ok
  end
end

private

def user_params
  # added require
  params.require(:user).permit(:username, :password, :password_confirmation, :bio, :avatar, :email)
end

My User model:

class User < ApplicationRecord
  has_secure_password
  has_many :anime_lists
  has_many :animes, through: :anime_lists
  has_many :manga_lists
  has_many :mangas, through: :manga_lists

  validates :username, presence: true, confirmation: {case_sensitive: false}, uniqueness: true, length: {in: 6..30}
  validates :password, presence: true, confirmation: true
end


Solution 1:[1]

I think your validations are the issue. Password presence is validating on every update. Since User#password is nil and you don't have a password in your params, it fails.

class User < ApplicationRecord
  has_secure_password
  validates :password, presence: true, confirmation: true
end
>> User.create(email: '[email protected]', password: '123456');
>> User.first.update!(email: '[email protected]')
  User Load (0.8ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
ActiveRecord::RecordInvalid: Validation failed: Password can't be blank

has_secure_password also adds its own validations.

https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password

If you want to customize password validations use

has_secure_password validations: false
# + your password validations

To get some ideas, you can take a look at how devise does validations:

https://github.com/heartcombo/devise/blob/v4.8.1/lib/devise/models/validatable.rb#L60

Solution 2:[2]

I can not be totally sure without knowing the data in your database but I would say you have two users with username=newperson.

When you try to save any change on any of those the validation triggers and the changes are not commited to the dabase.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Alex
Solution 2 javiyu