Restfulリソースの破棄時に、破棄操作の続行を許可する前に、いくつかのことを保証したいですか?基本的に、データベースが無効な状態になることに気付いた場合、破棄操作を停止する機能が必要ですか?破棄操作には検証コールバックがないので、破棄操作を受け入れる必要があるかどうかをどのように「検証」するのでしょうか。
Restfulリソースの破棄時に、破棄操作の続行を許可する前に、いくつかのことを保証したいですか?基本的に、データベースが無効な状態になることに気付いた場合、破棄操作を停止する機能が必要ですか?破棄操作には検証コールバックがないので、破棄操作を受け入れる必要があるかどうかをどのように「検証」するのでしょうか。
回答:
例外を発生させて、それをキャッチすることができます。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
これでfalsemyBooking.errors
が返され、戻り時に入力されます。
false
の最後にあるbefore_destroy
は役に立ちません。これからはthrow(:abort)
(@see:weblog.rubyonrails.org/2015/1/10/This-week-in-Rails/…)を使用する必要があります。
has_many :booking_payments, dependent: :restrict_with_error
注:
レール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
has_many :booking_payments, dependent: :restrict_with_error
それは私がRails5でしたことです:
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
has_many :qrcodes, dependent: :restrict_with_error
ActiveRecordアソシエーションhas_manyとhas_oneは、関連するテーブル行が削除時に削除されることを確認する依存オプションを許可しますが、これは通常、データベースが無効になるのを防ぐのではなく、データベースをクリーンに保つためです。
like_so
。
dependent
孤立したレコードが作成される場合にエンティティの削除を許可しないオプションもあります(これは質問により関連性があります)。例えばdependent: :restrict_with_error
コントローラの「if」ステートメントで破棄アクションをラップできます。
def destroy # in controller context
if (model.valid_destroy?)
model.destroy # if in model context, use `super`
end
end
valid_destroyはどこですか?レコードを破棄するための条件が満たされた場合にtrueを返すモデルクラスのメソッドです。
このような方法を使用すると、ユーザーに削除オプションが表示されないようにすることもできます。これにより、ユーザーが不正な操作を実行できなくなるため、ユーザーエクスペリエンスが向上します。
if
でステートメントをdestroy
代わりに呼び出すのを除いて、あなたのコントローラのアクションif model.valid_destroy?
だけで呼び出す、if model.destroy
など、成功したかどうかを破壊するモデルハンドルとlet
Rails 6現在の状況:
これは機能します:
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
動作しません:https://github.com/rails/rails/issues/32376
throw(:abort)
実行をキャンセルするにはRails5が必要なので、https://makandracards.com/makandra/20301-cancelling-the-activerecord-callback-chain
prepend: true
dependent: :destroy
検証が実行される前に実行されないようにするために必要です:https://github.com/rails/rails/issues/3458
他の回答やコメントからこれを一緒に釣ることができますが、どれも完全ではありませんでした。
補足として、多くの場合、has_many
孤立したレコードが作成される場合にレコードを削除しないようにする例として、リレーションを使用しました。これははるかに簡単に解決できます。
has_many :entities, dependent: :restrict_with_error
before_destroy :handle_destroy, prepend: true; before_destroy { throw(:abort) if errors.present? }
すぐにプロセスを破壊し、他のbefore_destroyの検証からのエラーが終わるのではなく通過することができます
私はここからコードを使用して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
これで、エンタープライズを削除すると、このプロセスはエンタープライズに関連付けられた製品があるかどうかを検証します。注:最初に検証するには、クラスの先頭にこれを書き込む必要があります。
Rails5で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
on: :destroy
を参照してください。この問題
これがサポートされることを望んでいたので、railsの問題を開いて追加しました。