nmadoug
nmadoug

Reputation: 47

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

For my Employee class, I have the following self-joinig associations:

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

And my employees DB table has the column replaces_employee_id.

In my app, if an employee gets a new role, the following occurs:

  1. Current employee record is cloned.
  2. <current record's>.replaces_employee = cloned record
  3. Cloned record is saved.
  4. Current employee record is saved to persist other data attributes (i.e. name change). During this save, Rails auto-magically updated the current record's replaces_employee_id column with the cloned record ID.

Yep, that magic was great and worked in my old Rails 4.2 app. Unfortunately this isn't working for my Rails 7.0 app. I've tried updating the belongs_to association with an explicit foreign key to no avail. Please note for existing DB records that do have replaces_employee_id populated, I have access to the associated ARs (i.e. Employee.find(1).replaces_employee works).

Any ideas why this isn't working for Rails 7? Was there some configuration on the 4.2 side that I'm missing?

Upvotes: 0

Views: 74

Answers (1)

Alex
Alex

Reputation: 30036

Besides that there is no update_attributes! method in rails 7, everything seem to work:

# 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>]

Upvotes: 0

Related Questions