How do I catch/solve an ActiveRecord:RecordInvalid exception caused when I save a built assocation

20,841

Solution 1

Here's the code that I used to solve the problem while giving the user good feedback on the why the save failed. Please forgive my inelegant ruby code.

One small problem remains . . . if the package and the location both fail validation, only the location error message is displayed on reload. If the user then corrects the location error but not the package error, he is shown the package error message. I'm working on how to show all of the errors on the first reload

  def create
    @package= current_user.package.build(params[:package])
    if package_location && @package.save
        flash[:success] = "Package created!"
        redirect_to root_path
      else
        render 'pages/home'
    end
  end

 def package_location
   gps_processing if !session[:gps_aware]
   location = @package.locations.build(:address => session[:address])
   if !location.valid?
     @package.errors.add(:address, "You have entered an invalid address") 
     return false
   else
      return true
   end
 end

 def gps_processing
   session[:address] = [params[:story][:street_address], params[:story][:city], 
          params[:story][:state], params[:story][:country]].compact.join(', ')
 end

Solution 2

The selected answer is not accurate. According to documentation here there's a simple way to catch rescue this exception:

begin
  complex_operation_that_calls_save!_internally
rescue ActiveRecord::RecordInvalid => invalid
  puts invalid.record.errors
end

You can access the messages instance variable of errors and get the field and error message associated.

Solution 3

A couple ideas off the top of my head:

Use @package.save! and a rescue block:

def create
  @package = current_user.package.build(params[:package])
  package_location
  @package.save!
  flash[:success] = "Package created!"
  redirect_to root_path
rescue      
  render 'pages/home'
end

Use validates_associated in your Package model, and only save if it's valid:

def create
  @package = current_user.package.build(params[:package])
  package_location

  # You might be able to just use if(@package.save), but I'm not positive.
  if(@package.valid?)
    @package.save!
    flash[:success] = "Package created!"
    redirect_to root_path
  else      
    render 'pages/home'
  end
end

And I'm sure there are a couple more ways, too, as you're working in Ruby...

Hope that helps!

Share:
20,841
chuck w
Author by

chuck w

Updated on July 09, 2022

Comments

  • chuck w
    chuck w almost 2 years

    I am hoping to get some help solving a problem that I'm sure many of you could avoid in your sleep.

    I have two models in a habtm relationship. A package can have many locations, and a location can have many packages. If my location model fails validation (due to an empty location address, for example), I get anActiveRecord:RecordInvalid exception. I understand that I'm getting this error because when I call package.save, rails automatically calls save! on the location association.

    I'm not sure how to avoid the error or at least rescue the error. Do any of you have any good advice, both on how to solve the problem and on Rails best practices?

    Here is the code:

    def create
        @package = current_user.package.build(params[:package])
        package_location
        if @package.save
          flash[:success] = "Package created!"
          redirect_to root_path
        else        
          render 'pages/home'
        end
      end
    
      def package_location
        gps_processing if !session[:gps_aware]
        @package.locations.build(:address => session[:address])
      end
    
      def gps_processing
        session[:address] = [params[:story][:street_address], params[:story][:city], params[:story][:state], params[:story][:country]].compact.join(', ')
      end
    
    class Package< ActiveRecord::Base
    
      belongs_to :user
      has_and_belongs_to_many :locations
    
      validates         :content,   :presence   => true, 
                        :length     => {:maximum => 140}
      validates      :user_id,    :presence => true
    
      default_scope :order => 'package.created_at DESC'
    
    end
    
    class Location < ActiveRecord::Base
    
      attr_accessible :lng, :lat, :address
    
      validates     :lng,       :presence   => true
      validates     :lat,       :presence   => true
      validates     :address,   :presence   => true
    
      geocoded_by :full_street_address, :latitude => :lat, :longitude => :lng  
    
      before_validation :geocode
    
      has_and_belongs_to_many :packages
    
      def full_street_address
        address
      end
    end
    

    ` Thanks in advance for your help!