Rails中获取字段旧值应使用哪个回调?状态变更触发邮件场景
问题分析与解决方案
原代码的核心问题在于使用了before_action回调,它会在update动作处理参数更新之前执行。此时@ad还是数据库里的旧数据,新的status值还没被赋值到对象上,所以@ad.status_published?判断的是旧状态,永远满足不了“新值为published”的条件,导致邮件无法发送。
方案一:控制器改用after_action
把回调改成after_action,确保在update动作完成、@ad已经更新后再执行判断逻辑:
class Shop::AdsController < Admin2Controller before_action :set_ad, only: %i[edit update destroy] after_action :send_notification_if_published, only: :update def send_notification_if_published # 用status_was获取旧值,判断旧值不是published且新值是published return unless @ad.status_was != 'published' && @ad.status == 'published' notification = Notification.find_by(action: 'ad_published') subscribed_users = User.joins(:notification_preferences) .where(notification_preferences: { notification_id: notification.id }) subscribed_users.each do |user| NotificationsMailer.new(user, notification, self).deliver_later end end end
方案二:将逻辑移至模型层(推荐)
业务逻辑放在模型层更符合MVC设计原则,也能让控制器更简洁,且逻辑适用场景更通用:
模型代码(示例路径:app/models/ad.rb):
class Ad < ApplicationRecord after_save :send_published_notification, if: :just_published? private def just_published? status_was != 'published' && status == 'published' end def send_published_notification notification = Notification.find_by(action: 'ad_published') return unless notification subscribed_users = User.joins(:notification_preferences) .where(notification_preferences: { notification_id: notification.id }) subscribed_users.each do |user| NotificationsMailer.new(user, notification, self).deliver_later end end end
控制器代码简化:
class Shop::AdsController < Admin2Controller before_action :set_ad, only: %i[edit update destroy] # 原有update动作保持不变,无需额外回调 end
这样不管是通过控制器更新,还是其他方式(比如后台任务、控制台)修改广告状态,只要满足“从非published变为published”的条件,都会自动触发邮件通知。
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

