2008-09-23 26 views
65

Khi hủy tài nguyên an toàn, tôi muốn đảm bảo một vài điều trước khi tôi cho phép hoạt động phá hủy tiếp tục? Về cơ bản, tôi muốn có khả năng ngừng hoạt động phá hủy nếu tôi lưu ý rằng làm như vậy sẽ đặt cơ sở dữ liệu ở trạng thái không hợp lệ? Không có callbacks xác nhận về một hoạt động phá hủy, do đó, làm thế nào một "xác nhận" cho dù một hoạt động phá hủy nên được chấp nhận?Làm cách nào để 'xác thực' khi hủy đường ray

+0

liên quan: http://stackoverflow.com/questions/5520320/validate-before-destroy –

Trả lời

55

Bạn có thể tăng ngoại lệ mà bạn bắt được. Rails kết thúc tốt đẹp xóa trong một giao dịch, giúp các vấn đề.

Ví dụ:

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 

Hoặc bạn có thể sử dụng callback before_destroy. Cuộc gọi lại này thường được sử dụng để hủy các bản ghi phụ thuộc, nhưng bạn có thể ném một ngoại lệ hoặc thêm một lỗi thay vào đó.

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 bây giờ sẽ trả về false, và myBooking.errors sẽ được áp dụng vào trở lại.

+0

Um, có gì sai với chỉ kiểm tra các hiệp hội booking_payments cần được xác định trên mô hình mà thay vì gọi BookingPayment .count, dẫn đến mã xấu? –

+0

Tôi đã chỉnh sửa trả lời của mình cho phù hợp. Xin lỗi, tôi đã kéo cái này ra khỏi một số mã cũ ... –

+3

Lưu ý rằng bây giờ nó nói "... ok, tiếp tục và tiêu diệt", bạn cần đặt "siêu", vì vậy phương pháp phá hủy ban đầu thực sự được gọi. –

3

Bạn cũng có thể sử dụng gọi lại before_destroy để tăng ngoại lệ.

5

Các liên kết ActiveRecord has_many và has_one cho phép tùy chọn phụ thuộc để đảm bảo các hàng trong bảng có liên quan bị xóa khi xóa, nhưng điều này thường giữ cho cơ sở dữ liệu của bạn sạch hơn là ngăn không hợp lệ.

+1

+1 cho "\ _" không nhận ra rằng đó là một tùy chọn trên SO – ahsteele

+1

Một cách khác để chăm sóc dấu gạch dưới, nếu chúng là một phần của tên hàm hoặc tương tự, là quấn chúng vào các dấu gạch chéo ngược. Điều đó sẽ hiển thị sau đó dưới dạng mã, 'like_so'. –

5

Bạn có thể quấn phá hủy hành động trong một "nếu" tuyên bố trong bộ điều khiển:

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

đâu valid_destroy? là một phương thức trên lớp mô hình của bạn trả về true nếu các điều kiện hủy phá bản ghi được đáp ứng.

Có phương pháp như vậy cũng sẽ cho phép bạn ngăn hiển thị tùy chọn xóa cho người dùng - điều này sẽ cải thiện trải nghiệm người dùng khi người dùng không thể thực hiện thao tác bất hợp pháp.

+6

Vòng lặp vô hạn, bất kỳ ai? – jenjenut233

+1

bắt tốt, nhưng tôi đã giả định phương pháp này là trong bộ điều khiển, trì hoãn mô hình. Nếu nó là trong mô hình chắc chắn sẽ gây ra vấn đề –

+0

hehe, xin lỗi bout rằng ... Tôi thấy những gì bạn có nghĩa là, tôi chỉ thấy "phương pháp trên lớp mô hình của bạn" và nhanh chóng nghĩ "uh oh", nhưng bạn đúng - tiêu diệt trên bộ điều khiển, điều đó sẽ hoạt động tốt. :) – jenjenut233

46

chỉ là một lưu ý:

Đối với đường ray 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 
+2

Một vấn đề với cách tiếp cận này là callback before_destroy có vẻ được gọi là * sau khi * tất cả các booking_payments đã bị phá hủy. – sunkencity

+4

Vé liên quan: https://github.com/rails/rails/issues/3458 @sunkencity bạn có thể khai báo before_destroy trước khi tuyên bố kết hợp để tạm thời tránh điều này. – lulalala

4

tôi đã kết thúc sử dụng mã từ vào đây để tạo một ghi đè can_destroy trên 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 

này đã cho biết thêm lợi ích của việc làm cho nó tầm thường để ẩn/hiển thị nút xóa trên ui

3

Tôi có các lớp hoặc các mô hình

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 

Bây giờ khi bạn xóa một doanh nghiệp quá trình này xác nhận nếu có những sản phẩm liên quan đến doanh nghiệp Lưu ý: Bạn phải viết này trong top của lớp để xác nhận nó Đầu tiên.

13

Đó là những gì tôi đã làm với 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 
+2

Đây là một bài viết hay giải thích hành vi này trong Rails 5: http://blog.bigbinary.com/2016/02/13/rails-5-does-not-halt-callback-chain-when-false-is- return.html –

1

Sử dụng ActiveRecord xác nhận bối cảnh trong 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 
Các vấn đề liên quan