使用自加入关联更新 Rails 记录不会更新自加入列

Updating Rails record with self-join association doesn't update self join column

提问人:nmadoug 提问时间:5/27/2023 更新时间:5/29/2023 访问量:65

问:

对于我的班级,我有以下自加入协会:Employee

class Employee < ApplicationRecord
  belongs_to :replaces_employee, class_name: "Employee", optional: true

  has_one :replaced_by_employee,
    class_name: 'Employee',
    foreign_key: 'replaces_employee_id'
end

我的数据库表有列。employeesreplaces_employee_id

在我的应用中,如果员工获得新角色,则会发生以下情况:

  1. 克隆当前员工记录。
  2. <current record's>.replaces_employee= 克隆的记录
  3. 克隆的记录已保存。
  4. 保存当前员工记录以保留其他数据属性(即名称更改)。在保存过程中,Rails 神奇地用克隆的记录 ID 自动更新了当前记录的列。replaces_employee_id

是的,这种魔力很棒,在我的旧 Rails 4.2 应用程序中有效。不幸的是,这不适用于我的 Rails 7.0 应用程序。我尝试使用显式外键更新关联,但无济于事。请注意,对于已填充的现有数据库记录,我可以访问关联的 AR(即 作品)。belongs_toreplaces_employee_idEmployee.find(1).replaces_employee

知道为什么这不适用于 Rails 7 吗?我缺少 4.2 端的一些配置吗?

Ruby-on-Rails 协会自 加入

评论

0赞 Alex 5/27/2023
你能说明什么不起作用吗?current = Employee.create!; current.replaces_employee = current.dup; current.save!; Employee.all.as_json #=> [{"id"=>1, "replaces_employee_id"=>2}, {"id"=>2, "replaces_employee_id"=>nil}]
0赞 titan2gman 5/27/2023
可以共享数据库迁移吗?
0赞 nmadoug 5/28/2023
@titan2gman - 我的数据库表是几年前创建的。之后,在数据库迁移 --> 中使用此语句添加该列。正如你所看到的,没有参考。在我的数据库中,此列只是一个;没有外键引用。employeesreplaces_employeeadd_column :employees, :replaces_employee_id, :integerinteger
0赞 nmadoug 5/28/2023
@Alex - 在我旧的 Rails 4.2 代码中,我有(当然是提炼的);我在 .调用将保存记录。当我打电话时,我可以在日志中看到。所以一切看起来都不错。temp_clone = create_deep_clone; self.replaces_employee = temp_clone; temp_clone.update_attributes!(bla bla bla); self.savedeep_cloneable#create_deep_cloneupdate_attributes!replaces_employeeself.saveUPDATE "employees" SET "<additional updates>", "replaces_employee_id"
0赞 nmadoug 5/28/2023
@Alex - 在我的新代码 Rails 7.2 中,我遵循相同的步骤,但日志排除并简单地更新了所有其他属性。replaces_employee_id

答:

0赞 Alex 5/29/2023 #1

除此之外,rails 7 中没有方法,一切似乎都有效:update_attributes!

# app/models/employee.rb

class Employee < ApplicationRecord
  belongs_to :replaces_employee, class_name: "Employee", optional: true
  has_one :replaced_by_employee, class_name: "Employee", foreign_key: "replaces_employee_id"

  def replace
    # how deep is deep clone? did you check for validation errors?
    other = deep_clone
    other.attributes = {name: "copy of #{name}"}
    # call `save!` or `update!` to raise validation errors
    # when you call `save` or `update` you need to check for `errors`
    #  employee.save
    #  employee.errors #=> ???
    update!({name: "new name",
             replaces_employee: other})
  end
end
>> Employee.create!
=> #<Employee:0x00007eff8c5cc5a0 id: 1, name: nil, replaces_employee_id: nil>
>> Employee.first.replace
=> true
>> Employee.all
=>
[#<Employee:0x00007eff7e4e08c0 id: 1, name: "replaced name", replaces_employee_id: 2>,
 #<Employee:0x00007eff7e4e0780 id: 2, name: "copy of ", replaces_employee_id: nil>]

>> 3.times { Employee.first.replace }
=> 3
# i just dont' know if that's how you expect this to work
>> Employee.all
=> 
[#<Employee:0x00007eff8c11ce60 id: 1, name: "new name", replaces_employee_id: 5>,
 #<Employee:0x00007eff8c11cd20 id: 2, name: "copy of ", replaces_employee_id: nil>,
 #<Employee:0x00007eff8c11cbe0 id: 3, name: "copy of new name", replaces_employee_id: 2>,
 #<Employee:0x00007eff8c11caa0 id: 4, name: "copy of new name", replaces_employee_id: 3>,
 #<Employee:0x00007eff8c11c960 id: 5, name: "copy of new name", replaces_employee_id: 4>]