2008-09-23 9 views
65

Об уничтожении ресурса, который я хочу сохранить, я хочу гарантировать несколько вещей, прежде чем я разрешу операцию уничтожения продолжить? В принципе, я хочу, чтобы остановить операцию уничтожения, если я заметил, что это приведет к тому, что база данных окажется недопустимой? Нет никаких обратных вызовов проверки на операцию уничтожения, поэтому как можно «подтвердить», следует ли принять операцию уничтожения?Как «проверять» на уничтожение в рельсах

+0

Связанный: http://stackoverflow.com/questions/5520320/validate-before-destroy – 2011-11-08 16:31:22

ответ

55

Вы можете создать исключение, которое вы затем поймаете. Rails wraps удаляет транзакции, что помогает.

Например:

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 теперь будет возвращать ложь, и myBooking.errors будет заполняться по возвращении.

+0

Гм, что случилось с просто проверить ассоциацию booking_payments, которая должна быть определена на этой модели вместо вызова BookingPayment .count, приводящий к уродливому коду? – 2008-09-24 00:37:25

+0

Я отредактировал свой ответ соответственно. Извините, я вытащил это из какого-то старого кода ... – 2008-09-24 18:26:27

+3

Обратите внимание, что там, где теперь говорится «... ОК, идите вперед и уничтожьте», вам нужно поставить «супер», поэтому на самом деле вызывается метод первоначального уничтожения. – 2009-08-19 15:13:02

3

Вы также можете использовать обратный вызов before_destroy для повышения исключения.

5

В ассоциациях ActiveRecord has_many и has_one разрешает зависимую опцию, которая гарантирует удаление связанных строк таблицы при удалении, но обычно это означает, что ваша база данных чиста, а не предотвращает ее недействительность.

5

Вы можете обернуть разрушающего действия в "если" заявление в контроллере:

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

Где valid_destroy? - это метод класса модели, который возвращает true, если выполнены условия для уничтожения записи.

Наличие такого способа также позволит вам предотвратить отображение пользователем опции удаления, что улучшит работу пользователя, так как пользователь не сможет выполнить незаконную операцию.

46

просто примечание:

Для рельсов 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 
4

Я закончил с использованием кода здесь, чтобы создать can_destroy переопределение на 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 

Это Добавленная преимущество сделать тривиальным, чтобы скрыть/показать кнопку удаления на ui

3

У меня есть эти классы или модели

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 

Теперь при удалении предприятия этот процесс проверяет, есть ли продукты, связанные с предприятиями Примечание: Вы должны написать это в верхней части класса для того, чтобы подтвердить его первый.

13

Это то, что я сделал с 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 
1

Использование ActiveRecord контекст проверки в 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