Các lựa chọn xóa bản ghi trong Ruby on Rails
Lời nói đầu Khi bắt đầu thiết lập các quan hệ rằng buộc nhau ở model trong Rails, thì chắc chắn sẽ có lúc bạn phải đối mặt với việc lựa chọn xóa các bản ghi có quan hệ với nhau. Để rõ hơn về việc này, chúng ta đi vào 1 ví dụ nho nhỏ: class Post < ActiveRecord : : Base has_many ...
Lời nói đầu
Khi bắt đầu thiết lập các quan hệ rằng buộc nhau ở model trong Rails, thì chắc chắn sẽ có lúc bạn phải đối mặt với việc lựa chọn xóa các bản ghi có quan hệ với nhau. Để rõ hơn về việc này, chúng ta đi vào 1 ví dụ nho nhỏ:
class Post < ActiveRecord::Base has_many :comments end class Comment < ActiveRecord::Base belongs_to :post end
Và bạn cố gắng xóa 1 bản ghi Post mà có chứa bản ghi Comment ở trong đó. Bán chắc chắn sẽ không thể thực hiện điều này được vì nó có quan hệ phụ thuộc ở đây.
Điều này cũng dễ hiểu thôi, ràng buộc này sẽ tránh các bản ghi phụ thuộc bị xóa 1 cách vô tình. Và bạn có thể thiết lập 1 vài kịch bản khi thực hiện hành động xóa bản ghi như trên dễ dàng hơn, đó là sử dụng từ khóa dependent trong model của bạn, chẳng hạn như ví dụ trên ta viết lại như sau:
class Post < ActiveRecord::Base has_many :comments, dependent: :destroy end
hoặc là
class Post < ActiveRecord::Base has_many :comments, dependent: :delete_all end
Nhưng 1 vấn đề mới phát sinh ở đây là bạn nên chọn destroy hay delete_all ở đây
Destroy
Destroy là option phổ biến nhất mà chúng ta thướng sử dụng trong model Rails, ví dụ thực tế thì: Khi bạn xóa 1 bài đăng trên facebook chẳng hạn, thì các bình luận cũng được xóa theo
class Post < ActiveRecord::Base has_many :comments, dependent: :destroy end class Comment < ActiveRecord::Base belongs_to :post after_destroy { puts "Callback Fired" } end
Cơ bản thì destroy sẽ gọi đến tất cả các quan hệ phụ thuộc trong model và thực hiện hành động xóa chúng. Một ưu điểm của việc sự dụng destroy là nó có thể gọi lại calbacks được khi có vấn đề về dữ liệu. Ví dụ: bạn có thể sự dụng gọi lại after_destroy để ghi lại xác nhận các bản ghi đang bị xóa
Delete_all
Về cơ bản thì delete_all xóa tất cả các các tùy chọn tương tự destroy nhưng sẽ không thực hiện callbacks được.
class Comment < ActiveRecord::Base belongs_to :post after_destroy { puts "Callback Fired" } end class Post < ActiveRecord::Base has_many :comments, dependent: :delete_all end
Nullify
Ngoài Destroy và Delele_all thì là Nullify thường ít được sử dụng hơn Nullify hiểu ngược lại với destroy 1 chút, có nghĩa là các bản ghi con sẽ không bị phá hủy sau khi ActiveRecord loại bỏ các bản ghi cha mẹ của chúng. Option này thường ít được sử dụng nên đa phân các Developer có thể k biết đến keyword này, thực ra mình cũng mới biết =))
class Post < ActiveRecord::Base has_many :comments, dependent: :nullify end
Restrict
Ngoài các cách kể trên, nếu bạn không muốn xóa bản ghi thì từ khóa restrict sẽ ngăn các bản ghi phụ thuộc bị xóa
- :restrict ngăn cản các bản ghi bị xóa nhưng sẽ không sinh ra 1 ngoại lệ hoặc lỗi
- :restrict_with_exceptions dừng bản ghi phụ thuộc khỏi xóa, trong khi sinh ra 1 ngoại lệ
class Post < ActiveRecord::Base has_many :comments, dependent: :restrict_with_exception end
2.1.2 :003 > p.destroy (0.2ms) BEGIN (2.3ms) ROLLBACK ActiveRecord::DeleteRestrictionError: Cannot delete record because of dependent comments
- :restrict_with_error gây ra lỗi khi bản ghi cha mẹ cố gắng được xóa
class Post < ActiveRecord::Base has_many :comments, dependent: :restrict_with_error end
2.1.2 :003 > p.destroy (0.2ms) BEGIN (0.2ms) ROLLBACK => false 2.1.2 :004 > p.errors => #<ActiveModel::Errors:0x000001016a9828 @base=#<Post id: 6, title: "Post 101", body: nil, created_at: "2015-01-31 13:19:36", updated_at: "2015-01-31 13:19:36">, @messages={:base=>["Cannot delete record because dependent comments exist"]}>
Tại sao bạn có thể muốn sử dụng ngoại lệ hay sinh ra lỗi ở đây? Cơ bản thì sẽ giúp người dùng có thể biết lý do tại sao các bản ghi cha mẹ không thể bị xóa