我试图通过以下方式在视图(例如,在update.js.erb中)中使用ActiveModel::Dirty / ActiveRecord::AttributeMethods::Dirty:
<% if @product.saved_change_to_attribute?(:name) %>
alert("Name changed!")
<% end %>
在我的控制器中,我有:
class ProductsController < ApplicationController
def update
@product = Product.find(params[:id])
respond_to do |format|
if @product.update(product_params)
format.js { render(:action => :show) }
else
format.js { render(:action => :update) }
end
end
end
private
def product_params
params.require(:product).permit(:price)
end
end
在我的模型中,我有:
class Product < ApplicationRecord
after_update :something_that_clears_changed_attributes
private
def something_that_clears_changed_attributes
...
self.reload
end
end
在上面的示例中,alert("Name changed!")
永远不会被触发。
实际上,似乎如果在“更新流”(在控制器内)期间存在(在模型级别)reload对象的“ after回调”或进一步更新或保存它,则您将无法不再依赖肮脏。也就是说,Dirty方法可以返回“意外”值,因为对象在流期间被“操纵”。
使用使用gem的模型会在回调中多次重载,更新或保存对象,因此在流程期间使Dirty模型“无效”(即使在Rails 5中使用attribute_before_last_save
方法也是如此)返回“意外”值)。
要解决此问题,您可以使用this post中的第一个示例(通过在更新前将变量中的更改属性保留下来以供以后使用),但是也许有更好的方法来使用Dirty模型
在流程中有多个重新加载,更新,保存或其他清除更改的属性的方法时,是否有关于如何依赖Dirty模型的想法?
答案 0 :(得分:0)
如果您要引用对ActiveRecord对象的先前更改(在更改持续存在并重新加载对象之后),我建议创建一个单独的表(例如product_dirties
)以跟踪所述更改。让我们以您的Product
模型为例:
首先,您将要创建一个product_dirties
表
create_table :product_dirties do |t|
t.references :product, index: true, foreign_key: true
t.jsonb :modifications
end
然后,添加ProductDirty
模型
class ProductDirty < ApplicationRecord
belongs_to :product, inverse_of: :product_dirties
validates_presence_of :product, :modifications
end
并更新您的Product
模型,使其包含新的关联以及在进行更改时创建脏记录的回调:
class Product < ApplicationRecord
has_many :product_dirties, inverse_of: :product
before_save :create_dirty_record, if: -> { changed? }
private
def create_dirty_record
# modifications will be saved in this format: {"name"=>"new name here!"}
attribute_changes = ActiveSupport::HashWithIndifferentAccess[self.changed.map{ |attr| [attr, self.method(attr).call] }]
if attribute_changes.present?
ProductDirty.find_or_create_by(product: self).update_attribute(:modifications, attribute_changes)
end
self.restore_attributes # <- add this if you'd like to revert the changes to the product and keep them separate in the `product_dirties` table
end
end
然后可以将一种方法添加到Product
模型中,以查找更改。由于我们实际上并未保留更改(请参见上面apply_dirty_record
旁的注释),因此我们在父模型(例如Product
)中添加了self.restore_attributes
方法(见下文)。>
def apply_dirty_record
dirty_record = ProductDirty.find_by(product: self)
self.assign_attributes(dirty_record.modifications) if dirty_record
end