How to rescue model transaction and show the user an error?

44,156
def exchange_status_with(address)
  ActiveRecord::Base.transaction do
   self.save!
   address.save!
  end
rescue ActiveRecord::RecordInvalid => exception
  # do something with exception here
end

FYI, an exception looks like:

#<ActiveRecord::RecordInvalid: Validation failed: Email can't be blank>

And:

exception.message
# => "Validation failed: Email can't be blank"

Side note, you can change self.save! to save!


Alternate solution if you want to keep your active model errors:

class MyCustomErrorClass < StandardError; end

def exchange_status_with(address)
  ActiveRecord::Base.transaction do
   raise MyCustomErrorClass unless self.save
   raise MyCustomErrorClass unless address.save
  end
rescue MyCustomErrorClass
  # here you have to check self.errors OR address.errors
end
Share:
44,156
NoDisplayName
Author by

NoDisplayName

Updated on December 30, 2020

Comments

  • NoDisplayName
    NoDisplayName over 3 years

    So imagine you have 2 models, Person and Address, and only one address per person can be marked as 'Main'. So if I wanna change a person's main address, I need to use a transaction, to mark the new one as main and unmark the old one. And as far as I know using transactions in controllers is not good so I have a special method in model, thats what I've got:

    AddressesController < ApplicationController
     def update
      @new_address = Address.find(params[:id])
      @old_address = Address.find(params[:id2])
      @new_address.exchange_status_with(@old_address)       
     end
    end
    

    Model:

    class Address < ActiveRecord::Base
      def exchange_status_with(address)
        ActiveRecord::Base.transaction do
         self.save!
         address.save!
        end     
      end
    end
    

    So thequestion is, if the transaction in the model method fails, I need to rescue it and notify the user about the error, how do I do that? Is there a way to make this model method return true or false depending on whether the transaction was successful or not, like save method does?

    I probably could put that transaction in the controller and render the error message in the rescue part, but I guess its not right or I could put that method in a callback, but imagine there is some reason why I cant do that, whats the alternative?

    PS dont pay attention to finding instances with params id and id2, just random thing to show that I have 2 instances