I have a situation where I would like access to an associated grandparent before the parent object is saved. I can think of several hacks, but I'm searching for a clean way to accomplish this. Take the following code as an illustration of my problem:
class Company < ActiveRecord::Base
has_many :departments
has_many :custom_fields
has_many :employees, :through => :departments
end
class Department < ActiveRecord::Base
belongs_to :company
has_many :employees
end
class Employee < ActiveRecord::Base
belongs_to :department
delegate :company, :to => :department
end
company = Company.find(1) # => <Company id: 1>
dept = company.departments.build # => <Department id: nil, company_id: 1>
empl = dept.employees.build # => <Employee id: nil, department_id: nil>
empl.company # => Employee#company delegated to department.company, but department is nil
I'm using Rails 3.2.15. I understand what is happening here, and I understand why empl.department_id is nil; though I wish Rails held a direct reference to the prospective association prior to calling save, such that the last line could be delegated through the unsaved department object. Is there a clean work around?
UPDATE: I've tried this in Rails 4 as well, here is a console session:
2.0.0-p247 :001 > company = Company.find(1)
Company Load (1.5ms) SELECT "companies".* FROM "companies" WHERE "companies"."id" = ? LIMIT 1 [["id", 1]]
=> #<Company id: 1, name: nil, created_at: "2013-10-24 03:36:11", updated_at: "2013-10-24 03:36:11">
2.0.0-p247 :002 > dept = company.departments.build
=> #<Department id: nil, name: nil, company_id: 1, created_at: nil, updated_at: nil>
2.0.0-p247 :003 > empl = dept.employees.build
=> #<Employee id: nil, name: nil, department_id: nil, created_at: nil, updated_at: nil>
2.0.0-p247 :004 > empl.company
RuntimeError: Employee#company delegated to department.company, but department is nil: #<Employee id: nil, name: nil, department_id: nil, created_at: nil, updated_at: nil>
2.0.0-p247 :005 > empl.department
=> nil
UPDATE 2: Here is a test project on github.
Please look into the :inverse_of
option for belongs_to
and has_many
. This option will handle two-way assignments when building and fetching associated records in different cases.
From Bi-directional associations for ActiveRecord::Associations::ClassMethods
in the docs:
Specifying the
:inverse_of
option on associations lets you tell Active Record about inverse relationships and it will optimise object loading.
I don't love this solution, but this seems to solve the problem:
empl = dept.employees.build { |e| e.association(:department).target = dept}
It turns out you can pass a block to build, and ActiveRecord will yield to the block with the newly created record. Who knows what ActiveRecord weirdness such will bring. I'm leaving the question open for now to see if there are better solutions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With