I have in Rails application several tables with foreign keys constraints. For example, every order belongs to a customer. There's a costumer_id column on the orders table.
When I delete a costumer with a placed order, because of database constraints, MySQL returns the error:
Mysql::Error: Cannot delete or update a parent row: a foreign key constraint fails (
orders
, CONSTRAINTorders_ibfk_2
FOREIGN KEY (customer_id
) REFERENCEScustomers
(id
))
And the ugly error pops up on the screen, with all stacktrace and those stuff ActiveRecord::StatementInvalid in DevicesController#destroy ...
I'd like to know if there's an elegant way to treat these constraint errors, giving a beautiful like "you can delete this object because it is associated to X"
How could I do it?
By convention, Rails assumes that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix _id added. The :foreign_key option lets you set the name of the foreign key directly:
You need to explicitly define them as part of your migrations. By convention, Rails assumes that the id column is used to hold the primary key of its tables. The :primary_key option allows you to specify a different column. For example, given we have a users table with guid as the primary key.
Here are a few things you should know to make efficient use of Active Record associations in your Rails applications: All of the association methods are built around caching, which keeps the result of the most recent query available for further operations. The cache is even shared across methods. For example:
-- Always delete the children first. Again, this is a pretty simple example, but try thinking about it when you have a whole series of foreign keys. Say half a dozen, or even more, of them. All of a sudden, if you aren’t familiar with the structure of your database it could take some time to get that order right.
React in the before destroy callback:
class Customer < ActiveRecord::Base
before_destroy :no_referenced_orders
has_many :orders
private
def no_referenced_orders
return if orders.empty?
errors.add_to_base("This customer is referenced by order(s): #{orders.map(&:number).to_sentence}")
false # If you return anything else, the callback will not stop the destroy from happening
end
end
In the controller:
class CustomersController < ApplicationController
def destroy
@customer = Customer.find(params[:id])
if @customer.destroy then
redirect_to customers_url
else
render :action => :edit
end
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With