レールで破棄を「検証」するにはどうすればよいですか
-
02-07-2019 - |
質問
休止状態のリソースを破棄する場合、破棄操作の続行を許可する前に、いくつかのことを保証したいと思いますか?基本的に、データベースが無効な状態になることに気付いた場合に、破棄操作を停止できる機能が必要です。破棄操作には検証コールバックがありません。では、破棄操作を受け入れるかどうかをどのように「検証」するのでしょうか?
解決
例外を発生させてそれをキャッチすることができます。Rails は削除をトランザクションでラップするので、これが重要です。
例えば:
class Booking < ActiveRecord::Base
has_many :booking_payments
....
def destroy
raise "Cannot delete booking with payments" unless booking_payments.count == 0
# ... ok, go ahead and destroy
super
end
end
あるいは、before_destroy コールバックを使用することもできます。このコールバックは通常、依存レコードを破棄するために使用されますが、代わりに例外をスローしたり、エラーを追加したりすることもできます。
def before_destroy
return true if booking_payments.count == 0
errors.add :base, "Cannot delete booking with payments"
# or errors.add_to_base in Rails 2
false
# Rails 5
throw(:abort)
end
myBooking.destroy
false を返すようになり、 myBooking.errors
戻ったときに設定されます。
他のヒント
ただのメモ:
レール3用
class Booking < ActiveRecord::Base
before_destroy :booking_with_payments?
private
def booking_with_payments?
errors.add(:base, "Cannot delete booking with payments") unless booking_payments.count == 0
errors.blank? #return false, to not destroy the element, otherwise, it will delete.
end
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
ActiveRecord の関連付け has_many および has_one では、削除時に関連するテーブル行が確実に削除されるようにする依存オプションが可能ですが、これは通常、データベースが無効になるのを防ぐためではなく、データベースをクリーンな状態に保つためです。
コントローラーの「if」ステートメントで破棄アクションをラップできます。
def destroy # in controller context
if (model.valid_destroy?)
model.destroy # if in model context, use `super`
end
end
どこ 有効_破壊? は、レコードを破棄する条件が満たされた場合に true を返すモデル クラスのメソッドです。
このようなメソッドを使用すると、ユーザーに削除オプションが表示されないようにすることもできます。これにより、ユーザーは違法な操作を実行できなくなり、ユーザー エクスペリエンスが向上します。
最終的に、ここのコードを使用して、activerecord に can_destroy オーバーライドを作成しました。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
これには、UI 上の削除ボタンの表示/非表示を簡単にするという追加の利点もあります。
before_destroy コールバックを使用して例外を発生させることもできます。
これらのクラスまたはモデルがあります
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
これで、エンタープライズを削除すると、このプロセスが企業に関連する製品があるかどうかを検証します。最初に検証するには、これをクラスの先頭に記述する必要があります。
Rails 5 で ActiveRecord コンテキスト検証を使用します。
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
これがサポートされることを期待していたので、Rails のイシューを開いて追加してもらいました。