您如何查看 Rails 中哪个关联模型触发了回调?

How can you see what associated model triggered a callback in Rails?

假设 Student belongs_to :contactTeacherbelongs_to :contact。假设 first_nameContact 中,并且 StudentTeacher 有一个名字 通过 Contact 并且有accepts_nested_attributes_for :contact.

after_commit 回调中,如果 first_name 发生变化,我想看看它是来自 Student 还是来自 Teacher。我不确定如何做到这一点,或者它是否可能。在 Studentafter_commit 中,previous_changes 没有 first_name。它只出现在 Contactafter_commit 中。所以在 Contactafter_commit 里面,我想看看它是来自 Student 还是来自 Teacher.

我该怎么做?有没有类似destroyed_by_association? When I search for "by_association" in the docs I don't see anything else, nor do I see anything else in Active Record Autosave Association.

的东西

注意:Contact 可能同时具有 Student Teacher,所以它不像简单地说,如果 Contact 没有 Student,它一定来自 Teacher.

如果您将相应的关联添加到 Contact,它们将可以在 after_commit 回调中访问:

class Student < ApplicationRecord
  belongs_to :contact
  accepts_nested_attributes_for :contact
end

class Teacher < ApplicationRecord
  belongs_to :contact
  accepts_nested_attributes_for :contact
end

class Contact < ApplicationRecord
  has_one :teacher
  has_one :student

  after_commit do
    p teacher || student
  end
end
>> Teacher.create(contact_attributes: { name: 'teacher' })
...
# after_commit START
#<Teacher id: 1, contact_id: 1>
# after_commit END
=> #<Teacher:0x00007fd28056a9f0 id: 1, contact_id: 1>

一个警告是,如果老师不在场,它将访问数据库:

>> Student.create(contact_attributes: { name: 'student' });
 ...
# after_commit START
  Teacher Load (0.4ms)  SELECT "teachers".* FROM "teachers" WHERE "teachers"."contact_id" =  LIMIT   [["contact_id", 2], ["LIMIT", 1]]
#<Student id: 1, contact_id: 2>
# after_commit END
=> #<Student:0x00007fd2837bd9c0 id: 1, contact_id: 2>

切换到多态关系也是一种解决方法:

class Student < ApplicationRecord
  has_one :contact, as: :contactable
  accepts_nested_attributes_for :contact
end

class Teacher < ApplicationRecord
  has_one :contact, as: :contactable
  accepts_nested_attributes_for :contact
end

# NOTE: to add plymorphic relationship add this to contact migration:
#       t.references :contactable, polymorphic: true
class Contact < ApplicationRecord
  belongs_to :contactable, polymorphic: true

  after_commit do
    p contactable
  end
end
>> Student.create(contact_attributes: { name: 'student' })
 ...
# after_commit START        
#<Student id: 1>            
# after_commit END          
=> #<Student:0x00007f4a85c0d1c8 id: 1>

>> Teacher.create(contact_attributes: { name: 'teacher' })
 ...
# after_commit START        
#<Teacher id: 1>            
# after_commit END          
=> #<Teacher:0x00007f4a884e2a08 id: 1>

更新

如果将回调放在 StudentTeacher 中,无论上述设置如何,您都可以访问所有内容。

多态:

class Teacher < ApplicationRecord
  has_one :contact, as: :contactable
  accepts_nested_attributes_for :contact

  after_commit do
    p self
    p contact
    p contact.previous_changes
  end
end
>> Teacher.create(contact_attributes: { name: 'teacher' })
 ...
# after_commit START
#<Teacher id: 1>
#<Contact id: 1, name: "teacher", contactable_type: "Teacher", contactable_id: 1>
{"id"=>[nil, 1], "name"=>[nil, "teacher"], "contactable_type"=>[nil, "Teacher"], "contactable_id"=>[nil, 1]}
# after_commit END
=> #<Teacher:0x00007fbf1bdd2db8 id: 1>

Belongs_to:

class Teacher < ApplicationRecord
  belongs_to :contact
  accepts_nested_attributes_for :contact

  after_commit do
    p self
    p contact
    p contact.previous_changes
  end
end
>> Teacher.create(contact_attributes: { name: 'teacher' })
   ...
# after_commit START        
#<Teacher id: 1, contact_id: 1>
#<Contact id: 1, name: "teacher">
{"id"=>[nil, 1], "name"=>[nil, "teacher"]}
# after_commit END          
=> #<Teacher:0x00007f9f2ce6a730 id: 1, contact_id: 1>

如果你必须在 Contact 中有 after_commit 你必须明确地告诉 Contact 更新来自 TeacherStudent:

class Teacher < ApplicationRecord
  belongs_to :contact
  accepts_nested_attributes_for :contact

  # NOTE: before saving, let contact know it's about to be updated
  #       by Teacher.
  before_save do
    contact.updated_from = :teacher
  end
end

class Contact < ApplicationRecord
  # NOTE: just a temporary attribute
  attr_accessor :updated_from

  after_commit do
    print 'Updated from: '
    p self.updated_from
  end
end
>> Teacher.create(contact_attributes: { name: 'teacher' })
 ...
# after_commit START
Updated from: :teacher      
# after_commit END
=> #<Teacher:0x00007fd6a40459c0 id: 1, contact_id: 1>

或者没有 before_save 教师回调:

>> Teacher.create(contact_attributes: { name: 'teacher', updated_from: 'teacher' })
 ...
Updated from: "teacher"  

或覆盖contact_attributes=方法:

class Teacher < ApplicationRecord
  belongs_to :contact
  accepts_nested_attributes_for :contact

  def contact_attributes= attributes
    attributes[:updated_from] = :teacher
    super
  end
end