具有嵌套属性创建的模型更改 before_validation 和 before_save 之间的关联类型属性

Model with nested attribute creation changes association type attribute between before_validation and before_save

我有一个模型 Guest,它采用 Numberplate 的嵌套属性。

Guest采用STI(单table继承)继承自Person。为了防止需要额外查询或加入另一个 table,我将人员类型保存在 Numberplate table 中名为 person_type 的字段中。这样,我就知道哪些车牌号属于客人,而不需要加入 table。

由于此设置,我需要在 Numberplate 上手动设置正确的 person_type(还有另一种人类型)。我将其实现为 Numberplate 模型上的 before_validation 回调:

before_validation do
    self.person_type = self.person.type
end

实际实现有点通用,但归结为这一点。 现在我将在控制台的一次调用中创建一个 GuestNumberplate 这一切都按预期工作:

Numberplate.new({person: Guest.new({company: Company.first}), plate: 'EFEF98'})

Numberplate 实例上的 person_type 正确设置为 "Guest"

当我有一个允许嵌套属性在一个请求中创建 GuestNumberplate 的 API 端点时,问题就出现了。奇怪的是 person_type 被存储为 "Person' 而不是预期的 "Guest" 。我调试了一下,person_typebefore_validation 回调中设置正确,但在该回调和 before_save 回调之间 person_type 突然更改为 "Person"。 我试图覆盖 person_type setter 并在设置时记录。奇怪的是 setter 在 2 个 ActiveRecord 回调之间没有被调用。

对于这种行为是如何引入的,以及这是否是 Rails 核心某处的错误,或者我没有考虑到某些事情,我感到有点吃惊。我目前的解决方法是在额外的 before_save 回调中也设置 person_type 但这感觉不是最佳解决方案。

相关机型:

class Guest < Person
    accepts_nested_attributes_for :numberplates
end

class Numberplate < ApplicationRecord
    before_validation do
        self.person_type = self.person.type
    end

    after_validation do
        # person_type is correctly set to "Guest"
        puts self.inspect
    end

    before_create do
        # person_type is now suddenly set to "Person"
        puts self.inspect
    end
end

所以我想通了。

Person class 我有这个:

has_many :numberplates, as: :person, inverse_of: :person

as: :person 部分不应该存在并引入了此行为。它存在于早期从多态性到 STI 的迁移,因此被遗忘了。一旦我删除它,一切都按预期工作。