How do I 'validate' on destroy in rails

前端 未结 11 1763
你的背包
你的背包 2020-11-28 06:33

On destruction of a restful resource, I want to guarantee a few things before I allow a destroy operation to continue? Basically, I want the ability to stop the destroy oper

相关标签:
11条回答
  • 2020-11-28 07:13

    I have these classes or models

    class Enterprise < AR::Base
       has_many :products
       before_destroy :enterprise_with_products?
    
       private
    
       def empresas_with_portafolios?
          self.portafolios.empty?  
       end
    end
    
    class Product < AR::Base
       belongs_to :enterprises
    end
    

    Now when you delete an enterprise this process validates if there are products associated with enterprises Note: You have to write this in the top of the class in order to validate it first.

    0 讨论(0)
  • 2020-11-28 07:14

    I ended up using code from here to create a can_destroy override on activerecord: https://gist.github.com/andhapp/1761098

    class ActiveRecord::Base
      def can_destroy?
        self.class.reflect_on_all_associations.all? do |assoc|
          assoc.options[:dependent] != :restrict || (assoc.macro == :has_one && self.send(assoc.name).nil?) || (assoc.macro == :has_many && self.send(assoc.name).empty?)
        end
      end
    end
    

    This has the added benefit of making it trivial to hide/show a delete button on the ui

    0 讨论(0)
  • 2020-11-28 07:17

    It is what I did with Rails 5:

    before_destroy do
      cannot_delete_with_qrcodes
      throw(:abort) if errors.present?
    end
    
    def cannot_delete_with_qrcodes
      errors.add(:base, 'Cannot delete shop with qrcodes') if qrcodes.any?
    end
    
    0 讨论(0)
  • 2020-11-28 07:23

    I was hoping this would be supported so I opened a rails issue to get it added:

    https://github.com/rails/rails/issues/32376

    0 讨论(0)
  • 2020-11-28 07:25

    State of affairs as of Rails 6:

    This works:

    before_destroy :ensure_something, prepend: true do
      throw(:abort) if errors.present?
    end
    
    private
    
    def ensure_something
      errors.add(:field, "This isn't a good idea..") if something_bad
    end
    

    validate :validate_test, on: :destroy doesn't work: https://github.com/rails/rails/issues/32376

    Since Rails 5 throw(:abort) is required to cancel execution: https://makandracards.com/makandra/20301-cancelling-the-activerecord-callback-chain

    prepend: true is required so that dependent: :destroy doesn't run before the validations are executed: https://github.com/rails/rails/issues/3458

    You can fish this together from other answers and comments, but I found none of them to be complete.

    As a sidenote, many used a has_many relation as an example where they want to make sure not to delete any records if it would create orphaned records. This can be solved much more easily:

    has_many :entities, dependent: :restrict_with_error

    0 讨论(0)
  • 2020-11-28 07:25

    Use ActiveRecord context validation in Rails 5.

    class ApplicationRecord < ActiveRecord::Base
      before_destroy do
        throw :abort if invalid?(:destroy)
      end
    end
    
    class Ticket < ApplicationRecord
      validate :validate_expires_on, on: :destroy
    
      def validate_expires_on
        errors.add :expires_on if expires_on > Time.now
      end
    end
    
    0 讨论(0)
提交回复
热议问题