it-swarm.com.de

Wie 'validiere' ich beim Zerstören in rails

Bei der Zerstörung einer erholsamen Ressource möchte ich einige Dinge garantieren, bevor ich zulasse, dass eine Zerstörungsoperation fortgesetzt wird. Grundsätzlich möchte ich die Möglichkeit haben, den Destroy-Vorgang zu stoppen, wenn ich feststelle, dass die Datenbank dadurch in einen ungültigen Zustand versetzt wird. Es gibt keine Validierungsrückrufe für eine Destroy-Operation. Wie "validiert" man also, ob eine Destroy-Operation akzeptiert werden soll?

72
Stephen Cagle

Sie können eine Ausnahme auslösen, die Sie dann abfangen. Rails bricht Löschvorgänge in eine Transaktion ein, was hilfreich ist.

Beispielsweise:

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

Alternativ können Sie den before_destroy-Callback verwenden. Dieser Rückruf wird normalerweise verwendet, um abhängige Datensätze zu löschen. Sie können jedoch stattdessen eine Ausnahme auslösen oder einen Fehler hinzufügen.

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 wird nun false zurückgeben und myBooking.errors wird bei der Rückgabe ausgefüllt.

63
Airsource Ltd

nur eine Notiz:

Für Rails 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
47
workdreamer

Das habe ich mit Rails 5 gemacht:

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
18

Die ActiveRecord-Zuordnungen has_many und has_one ermöglichen eine abhängige Option, die sicherstellt, dass verwandte Tabellenzeilen beim Löschen gelöscht werden. Dies dient jedoch normalerweise dazu, die Datenbank sauber zu halten, anstatt zu verhindern, dass sie ungültig wird.

6
go minimal

Sie können die Destroy-Aktion in eine "if" -Anweisung im Controller einschließen:

def destroy # in controller context
  if (model.valid_destroy?)
    model.destroy # if in model context, use `super`
  end
end

Dabei ist valid_destroy? eine Methode in Ihrer Modellklasse, die true zurückgibt, wenn die Bedingungen zum Löschen eines Datensatzes erfüllt sind.

Mit einer solchen Methode können Sie auch verhindern, dass dem Benutzer die Löschoption angezeigt wird. Dies verbessert die Benutzerfreundlichkeit, da der Benutzer keine illegalen Vorgänge ausführen kann.

5
Toby Hede

Am Ende habe ich Code von hier verwendet, um eine can_destroy-Überschreibung für activerecord zu erstellen: 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

Dies hat den zusätzlichen Vorteil, dass es einfach ist, eine Löschschaltfläche auf der Benutzeroberfläche auszublenden/anzuzeigen

4
Hugo Forte

Ich habe diese Klassen oder Modelle

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

Wenn Sie nun ein Unternehmen löschen, überprüft dieser Prozess, ob mit Unternehmen verknüpfte Produkte vorhanden sind. Hinweis: Sie müssen dies oben in die Klasse schreiben, um es zuerst zu überprüfen.

3
Mateo Vidal

Sie können auch den before_destroy-Rückruf verwenden, um eine Ausnahme auszulösen.

3
MattW.

Verwenden Sie die ActiveRecord-Kontextüberprüfung 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
2
swordray

Ich hatte gehofft, dass dies unterstützt wird, also habe ich eine Rails Ausgabe geöffnet, um es hinzuzufügen:

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

1
ragurney