Các lựa chọn xóa bản ghi trong Ruby on Rails
Bài đăng này đã không được cập nhật trong 6 năm
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
Tài liệu tham khảo
http://guides.rubyonrails.org/association_basics.html#dependent https://alexhandley.co.uk/rails-dependent-associations/ https://stackoverflow.com/questions/2797339/rails-dependent-destroy-vs-dependent-delete-all https://stackoverflow.com/questions/22757450/difference-between-destroy-and-delete
All rights reserved