问题
I have a model Country and therefore a table countries. The countries table act as a collection of iso country and currency codes and should never reduce there content (after I have filled it with seed data). Because Country is a subclass of ActiveRecord::Base it inherits class methods like destroy, delete_all and so forth which deletes records. I'm looking for a solution to prevent the deletion of records at the model level.
Ofc. I know that I can make use of the object oriented approach to solve this problem by overriding this methods (and raise for instance an error when they called), but this assumes that I have to know all the inherited methods of the base class. I would be glad if someone could offer a more elegant solution.
回答1:
There's a before_destroy callback, maybe you could take advantage of that.
before_destroy :stop_destroy
def stop_destroy
self.errors[:base] << "Countries cannot be deleted"
return false
end
回答2:
Taking inspiration from Mark Swardstrom answer, I propose the following that is working also on Rails > 5.0:
Within your model:
before_destroy :stop_destroy
def stop_destroy
errors.add(:base, :undestroyable)
throw :abort
end
The following will make all calls to model.destroy
return false
and your model will not be deleted.
You can argue that still, calls to model.delete
will work, and delete your record, but since these are lower level calls this makes perfectly sense to me.
You could also delete the records directly from the database if you want, but the above solution prevents deletion from the application level, which is the right place to check that.
Rubocop checks your calls to delete or delete_all and raises a warning, so you can be 100% sure that if you call model.delete
is because you really want it.
My solution works on latest Rails versions where you need to throw :abort
instead of returning false.
回答3:
In Rails 6 this is how I prevent records from being deleted. The exception raised will roll back the transaction that is being used by ActiveRecord therefore preventing the record being deleted
class MenuItem < ApplicationRecord
after_destroy :ensure_home_page_remains
class Error < StandardError
end
protected #or private whatever you need
#Raise an error that you trap in your controller to prevent your record being deleted.
def ensure_home_page_remains
if menu_text == "Home"
raise Error.new "Can't delete home page"
end
end
So the ensure_home_page_remains method raises an MenItem::Error
that causes the transaction to be rolled back which you can trap in your controller and take whatever appropriate action you feel is necessary, normally just show the error message to the user after redirecting to somewhere. e.g.
# DELETE /menu_items/1
# DELETE /menu_items/1.json
def destroy
@menu_item.destroy
respond_to do |format|
format.html { redirect_to admin_menu_items_url, notice: 'Menu item was successfully destroyed.' }
format.json { head :no_content }
end
end
#Note, the rescue block is outside the destroy method
rescue_from 'MenuItem::Error' do |exception|
redirect_to menu_items_url, notice: exception.message
end
private
#etc...
来源:https://stackoverflow.com/questions/39280408/rails-how-to-prevent-the-deletion-of-records