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, :longit开发者_如何学JAVAude => :lng
before_validation :geocode
has_and_belongs_to_many :packages
def full_street_address
address
end
end
` Thanks in advance for your help!
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.
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!
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
精彩评论