【问题标题】:How to detect changes in has_many through association?如何通过关联检测has_many的变化?
【发布时间】:2020-04-12 21:48:23
【问题描述】:

我有以下型号。

class Company < ApplicationRecord
  has_many :company_users
  has_many :users, :through => :company_users

  after_update :do_something

  private

  def do_something
    # check if users of the company have been updated here
  end
end

class User < ApplicationRecord
  has_many :company_users
  has_many :companies, :through => :company_users
end

class CompanyUser < ApplicationRecord
  belongs_to :company
  belongs_to :user
end

然后我有这些种子:

Company.create :name => 'Company 1'
User.create [{:name => 'User1'}, {:name => 'User2'}, {:name => 'User3'}, {:name => 'User4'}]

假设我要更新公司 1 的用户,我将执行以下操作:

Company.first.update :users => [User.first, User.second]

这将按预期运行,并将在 CompanyUser 模型上创建 2 条新记录。

但是如果我想再次更新呢?就像运行以下命令:

Company.first.update :users => [User.third, User.fourth]

这将销毁前 2 条记录,并在 CompanyUser 模型上创建另外 2 条记录。

问题是我已经在技术上“更新”Company 模型,那么我如何在Company 模型上使用after_update 方法检测这些变化?

但是,更新属性就可以了:

Company.first.update :name => 'New Company Name'

我怎样才能让它也适用于关联?

到目前为止,我尝试了以下方法,但无济于事:

【问题讨论】:

    标签: ruby-on-rails activerecord rails-activerecord has-many-through ruby-on-rails-6


    【解决方案1】:

    在has_many关系上有一个集合回调before_add、after_add。

    class Project
      has_many :developers, after_add: :evaluate_velocity
    
      def evaluate_velocity(developer)
        #non persisted developer
        ...
      end
    end
    

    更多详情:https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Association+callbacks

    【讨论】:

    • 不幸的是,will not work 变成了Company.first.update(users: [User.first, User.second])
    【解决方案2】:

    您可以为此使用attr_accessor 并检查它是否已更改。

    class Company < ApplicationRecord
      attr_accessor :user_ids_attribute
    
      has_many :company_users
      has_many :users, through: :company_users
    
      after_initialize :assign_attribute
      after_update :check_users
    
      private
    
      def assign_attribute
        self.user_ids_attribute = user_ids
      end
    
      def check_users
        old_value = user_ids_attribute
    
        assign_attribute
    
        puts 'Association was changed' unless old_value == user_ids_attribute
      end
    end
    

    现在关联更改后,您将在控制台中看到消息。

    您可以将puts 更改为任何其他方法。

    【讨论】:

      【解决方案3】:

      我感觉您问错了问题,因为您无法在不破坏当前关联的情况下更新关联。正如你所说:

      This will destroy the first 2 records and will create another 2 records on CompanyUser model.

      知道我会建议您尝试以下代码:

      Company.first.users << User.third
      

      这样您就不会覆盖当前的关联。 如果您想一次添加多条记录,请尝试用 [ ] 或 ( ) 将它们包装起来,不确定要使用哪一条。

      你可以在这里找到文档:https://guides.rubyonrails.org/association_basics.html#has-many-association-reference

      希望对您有所帮助。

      编辑:

      好吧,我认为这不是你真正的问题。

      可能有 2 个解决方案:

      #1 观察者

      我所做的是您的联接表上的观察者,负责在每次更改 CompanyUser 时“ping”您的 Company 模型。

      gem rails-observers

      在这个观察者内部调用一个服务或任何你喜欢的东西,它将对值做你想做的事情

      class CompanyUserObserver < ActiveRecord::Observer
      
        def after_save(company_user)
          user = company_user.user
          company = company_user.company
          ...do what you want
        end
      
        def before_destroy(company_user)
          ...do what you want
        end
      end
      

      您可以根据需要使用多个回调。

      #2 保留记录

      事实证明你需要它保存记录。也许您应该考虑使用像 PaperTrailAudited 这样的 gem 来跟踪您的更改。

      很抱歉给您带来了困惑。

      【讨论】:

        猜你喜欢
        • 2012-10-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多