在防止删除之前修改 ActiveRecord 模型
Modifying ActiveRecord models before preventing deletion
我的应用程序中的某些记录分配有 DOI,在这种情况下不应删除它们。相反,他们应该更改他们的描述,并在用户触发他们的删除时被标记。我认为,相关模型中的一种方法如下:
before_destroy :destroy_validation
private
def destroy_validation
if metadata['doi'].blank?
# Delete as normal...
nil
else
# This is a JSON field.
modified_metadata = Marshal.load(Marshal.dump(metadata))
description = "Record does not exist anymore: #{name}. The record with identifier content #{doi} was invalid."
modified_metadata['description'] = description
modified_metadata['tombstone'] = true
update_column :metadata, modified_metadata
raise ActiveRecord::RecordNotDestroyed, 'Records with DOIs cannot be deleted'
end
end
这确实可以防止删除,但记录之后看起来没有变化,而不是有修改的描述。这是一个测试示例:
test "records with dois are not deleted" do
record = Record.new(metadata: metadata)
record.metadata['doi'] = 'this_is_a_doi'
assert record.save
assert_raises(ActiveRecord::RecordNotDestroyed) { record.destroy! }
assert Record.exists?(record.id)
modified_record = Record.find(record.id)
puts "#{record.description}" # This is correctly modified as per the callback code.
puts "#{modified_record.description}" # This is the same as when the record was created.
end
我只能猜测 Rails 正在回滚 update_column
由于出现异常,尽管我可能弄错了。我可以做些什么来防止这种情况发生?
save and destroy are automatically wrapped in a transaction
https://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
因此 destroy
失败,事务被回滚并且您在测试中看不到更新的列。
您可以尝试使用 after_rollback
回调 https://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_rollback
或 record.destroy
检查 record.errors
,如果发现手动更新记录 record.update_doi if record.errors.any?
.
before_destroy :destroyable?
...
def destroyable?
unless metadata['doi'].blank?
errors.add('Doi is not empty.')
throw :abort
end
end
def update_doi
modified_metadata = Marshal.load(Marshal.dump(metadata))
description = "Record does not exist anymore: #{name}. The record with identifier content #{doi} was invalid."
modified_metadata['description'] = description
modified_metadata['tombstone'] = true
update_column :metadata, modified_metadata
end
提示:使用 record.reload
而不是 Record.find(record.id)
。
我的应用程序中的某些记录分配有 DOI,在这种情况下不应删除它们。相反,他们应该更改他们的描述,并在用户触发他们的删除时被标记。我认为,相关模型中的一种方法如下:
before_destroy :destroy_validation
private
def destroy_validation
if metadata['doi'].blank?
# Delete as normal...
nil
else
# This is a JSON field.
modified_metadata = Marshal.load(Marshal.dump(metadata))
description = "Record does not exist anymore: #{name}. The record with identifier content #{doi} was invalid."
modified_metadata['description'] = description
modified_metadata['tombstone'] = true
update_column :metadata, modified_metadata
raise ActiveRecord::RecordNotDestroyed, 'Records with DOIs cannot be deleted'
end
end
这确实可以防止删除,但记录之后看起来没有变化,而不是有修改的描述。这是一个测试示例:
test "records with dois are not deleted" do
record = Record.new(metadata: metadata)
record.metadata['doi'] = 'this_is_a_doi'
assert record.save
assert_raises(ActiveRecord::RecordNotDestroyed) { record.destroy! }
assert Record.exists?(record.id)
modified_record = Record.find(record.id)
puts "#{record.description}" # This is correctly modified as per the callback code.
puts "#{modified_record.description}" # This is the same as when the record was created.
end
我只能猜测 Rails 正在回滚 update_column
由于出现异常,尽管我可能弄错了。我可以做些什么来防止这种情况发生?
save and destroy are automatically wrapped in a transaction https://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
因此 destroy
失败,事务被回滚并且您在测试中看不到更新的列。
您可以尝试使用 after_rollback
回调 https://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_rollback
或 record.destroy
检查 record.errors
,如果发现手动更新记录 record.update_doi if record.errors.any?
.
before_destroy :destroyable?
...
def destroyable?
unless metadata['doi'].blank?
errors.add('Doi is not empty.')
throw :abort
end
end
def update_doi
modified_metadata = Marshal.load(Marshal.dump(metadata))
description = "Record does not exist anymore: #{name}. The record with identifier content #{doi} was invalid."
modified_metadata['description'] = description
modified_metadata['tombstone'] = true
update_column :metadata, modified_metadata
end
提示:使用 record.reload
而不是 Record.find(record.id)
。