after_create无权访问在before_created回调期间创建的关联记录?

时间:2017-05-30 09:28:06

标签: ruby-on-rails callback

我遇到了一个奇怪的问题,阅读回调RoR指南没有给我答案。

class User < ActiveRecord::Base
...
has_many :company_users, dependent: :destroy
has_many :companies, through: :company_users
has_many :user_teams, dependent: :destroy
has_many :teams, through: :user_teams

before_create :check_company!
after_create :check_team

def check_company!
  return if self.companies.present?
  domain = self.email_domain
  company = Company.find_using_domain(domain)
  if company.present?
    assign_company(company)
  else
    create_and_assign_company(domain)
  end
end 

def check_team
  self.companies.each do |company|
    #do stuff
  end
end
...
end

after_create:check_team回调面临问题,因为行

self.companies.each do |company|

这里,即使创建了公司和用户并且用户与之关联,self.companies也会返回一个空数组[]。我知道我可以通过使它成为before_create回调来解决它。但我很困惑!

为什么after_create回调在提交后无法访问自己的关联?

解决方案:请在接受的答案中阅读我的评论,以了解问题的原因和解决方案。

1 个答案:

答案 0 :(得分:1)

  • before_create内部回调中,记录的id尚未可用,因为它在...之前创建...所以它还没有在数据库中持久存在id。这意味着关联的company_user记录尚未具有user_id值,正是因为此时user.id仍为nil。但是,Rails让你不必担心这个&#34;鸡蛋和鸡蛋&#34;问题,只要你做得正确:

  • 我重新创建了您的设置(CompanyUserCompanyUser模型),以下内容适合您的情况(经过测试的工作):

    class User < ApplicationRecord
      has_many :company_users, dependent: :destroy
      has_many :companies, through: :company_users
    
      before_create :check_company!
      after_create :check_team
    
      def check_company!
        # use `exists?` instead of `present?` because `exists?` is a lot faster and efficient as it generates with a `LIMIT 1` SQL.
        return if companies.exists?
    
        ## when assigning an already persisted Company record:
        example_company = Company.first
        # 1) WORKS
        companies << example_company
        # 2) WORKS
        company_users.build(company: example_company)
    
        ## when assigning and creating a new Company record:
        # 1) WORKS (this company record will be automatically saved/created after this user record is saved in the DB)
        companies.build(name: 'ahaasdfwer') # or... self.companies.new(name: 'ahaasdfwer')
        # 2) DOES NOT WORK, because you'll receive an error `ActiveRecord::RecordNotSaved: You cannot call create unless the parent is saved`
        companies.create(name: 'ahaasdfwer')
      end
    
      def check_team
        puts companies.count
        # => 1 if "worked"
        puts companies.first.persisted?
        # => true if "worked"
      end
    end