如何检测Mongoid的HABTM关系变更并在添加对象时发送邮件?
Hey there! Let's tackle this problem step by step—we'll set up logic to detect when a User is added to an Account's managers HABTM association and trigger an email notification.
1. Track Changes to the HABTM Association
Mongoid's HABTM associations rely on the underlying _ids field (in this case, manager_ids on the Account model) to track related records. We can use Mongoid's built-in change tracking methods to compare the state of this field before and after a save.
Add these callbacks and helper methods to your Account model:
class Account include Mongoid::Document has_and_belongs_to_many :managers, class_name: 'User', inverse_of: :accounts # Store the previous state of manager_ids before saving before_save :store_previous_manager_ids # Trigger email logic only if manager_ids has changed after_save :send_manager_added_emails, if: -> { manager_ids_changed? } private def store_previous_manager_ids # Save a copy of the original manager_ids to compare later @previous_manager_ids = manager_ids_was.dup end def send_manager_added_emails # Calculate which manager IDs were newly added new_manager_ids = manager_ids - @previous_manager_ids return if new_manager_ids.empty? # Fetch the corresponding User records new_managers = User.find(new_manager_ids) # Send a notification email to each newly added manager new_managers.each do |manager| AccountMailer.manager_added_notification(self, manager).deliver_now end end end
Key Details:
manager_ids_was: Mongoid provides this method to access the value ofmanager_idsbefore any changes were made. We duplicate it to avoid reference issues.manager_ids_changed?: A built-in predicate method that returnstrueif themanager_idsfield has been modified since the last save.- We calculate
new_manager_idsby subtracting the previous IDs from the updated IDs—this gives us exactly the users added to the association.
2. Set Up the Email Notification
Next, create a mailer to handle the notification. Here's an example of what AccountMailer might look like:
class AccountMailer < ApplicationMailer def manager_added_notification(account, manager) @account = account @manager = manager mail( to: manager.email, subject: "You've been added as a manager for #{account.name}" ) end end
Don't forget to create the corresponding email template (e.g., app/views/account_mailer/manager_added_notification.html.erb) with the content you want to send to the new manager.
3. Test the Workflow
Using your console example, here's how this would work:
[15] pry(main)> a.reload => #<Account _id: 5ae71215a8abbc6378b4bee8, code: "ACC5", name: "Account5", manager_ids: []> [16] pry(main)> u.reload => #<User _id: 5ae59b5da8abbc6f3d880a10, name: "Manager 1", account_ids: []> [17] pry(main)> a.managers << u [18] pry(main)> a.save
After running a.save, the send_manager_added_emails method will detect that manager_ids changed from an empty array to include u.id, fetch the User record, and send the notification email.
Bonus: Handling Bulk Additions
This logic works even if you add multiple managers at once (e.g., a.managers << u1 << u2)—it will identify all newly added users and send emails to each one.
内容的提问来源于stack exchange,提问作者tomatito

