如何在Rails控制器中正确使用ActiveModel Dirty触发PDF生成
解决方案
方案一:在update前预存属性差异
因为update执行后会清除脏数据标记,所以要先获取原记录属性与待更新参数的差异,排除updated_at后判断是否有实际业务属性变更:
def update # 提前获取原记录属性,排除自动更新的updated_at original_attrs = @individual.attributes.except('updated_at') # 计算待更新参数中与原属性不同的字段,同样排除updated_at actual_changes = individual_params.to_h.except('updated_at').select do |key, val| original_attrs[key] != val end respond_to do |format| if @individual.update(individual_params) format.html { redirect_to @individual, notice: "Individual was successfully updated." } # 仅当存在非updated_at的属性变更时调用PDF生成方法 render_pdf if actual_changes.present? else format.html { render :edit, status: :unprocessable_entity } end end end
方案二:利用changed_attributes在update后校验
Rails中,update执行后changed_attributes方法会保留本次更新的变更记录(直到下一次重载记录),可以通过它过滤掉updated_at来判断:
def update respond_to do |format| if @individual.update(individual_params) format.html { redirect_to @individual, notice: "Individual was successfully updated." } # 过滤掉updated_at,检查是否有其他属性变更 non_timestamp_changes = @individual.changed_attributes.except('updated_at') render_pdf if non_timestamp_changes.present? else format.html { render :edit, status: :unprocessable_entity } end end end
关于after_update_commit回调的触发逻辑
after_update_commit仅当记录的属性(包括自动更新的updated_at/created_at)发生实际变更时才会触发:
- 如果调用
update但没有任何属性值变化(包括时间戳),Rails会跳过数据库更新操作,回调不会执行; - 如果只有
updated_at被更新(比如调用touch方法,或其他操作触发时间戳自动更新),此时记录确实发生了属性变更,回调会被触发。
如果要用回调实现需求,需要在回调内过滤updated_at,同时注意模型无法直接调用控制器的render_pdf方法,需将PDF生成逻辑封装为独立服务或模型方法:
# 在Individual模型中 after_update_commit :generate_pdf_if_needed private def generate_pdf_if_needed # 检查是否存在除updated_at外的变更 unless (changed_attributes.keys - ['updated_at']).empty? IndividualPdfGenerator.new(self).generate end end
内容的提问来源于stack exchange,提问作者Jerome
相关产品推荐
相关产品推荐

