Valit Laiho
Valit Laiho

Reputation: 29

How do I prevent model update from changing an attribute?

I have Account model with attribute role. Roles wrote with enum role: [:user, :admin]. I want that if left one account with role admin he can't update his role to user. I think need to write something like this: @account = Account.where(role: params[: role])... and then I don't know.

account.rb

class Account < ApplicationRecord
  enum role: [:user, :admin]
end

accounts_controller.rb

class AccountsController < ApplicationController
  def index
    @accounts = Account.all
  end
  def update
    @account = Account.find(params[:id])
    redirect_to accounts_path if account.update(role: params[:role])
  end
end

schema.rb

create_table "accounts", force: :cascade do |t|
  t.integer "role", default: 0
end

Upvotes: 1

Views: 1730

Answers (1)

lacostenycoder
lacostenycoder

Reputation: 11186

I think what you want is a model callback before_update that acts like a validation.

class Account < ApplicationRecord
  enum role: [:user, :admin]

  before_update :insure_admin, if: -> { role == :admin && role_changed? }

  private
  def insure_admin
    errors.add(:role, "admin cannot switch to regular user")
  end
end

This should prevent the account.update(role: params[:role]) from returning true but you'll probably want to handle the error in your controller, something like:

class AccountsController < ApplicationController
  def index
    @accounts = Account.all
  end
  def update
    @account = Account.find(params[:id])
    if account.update(role: params[:role])
      redirect_to accounts_path 
    else
      redirect_to :back, flash: account.errors.full_messages
    end
  end
end

You might also want to add front end form validation to not allow the form to change role if the account is already persisted.

Upvotes: 2

Related Questions