How do you override the getter method for a Rails has_one association?
Asked Answered
T

2

8

I want to override/extend a Rails has_one association method so that it always returns an instance of the associated class. (If none already exists in the database, I want to create a new one and assign it to the parent instance.)

Ideally, I'd like to do this through the built-in Rails association extension mechanism. However, I don't know the name of the "getter" method, and so I don't know what to override.

How do I override the association getter so that I can instantiate a new object when it's nil?

Technique answered 24/10, 2012 at 17:58 Comment(3)
Overriding the has_one association macro may lead to tears. I go with 1) a migration that backfills missing data and 2) an observer with an after_create listener on the parent resource.Grison
@TomL: I'll keep that in mind, but in this particular case, I'd like the actual instance (not just the data) to be available immediately when referenced (and not exist before).Technique
You could handle it in the parent controller. In the new action do something like @parent = Parent.new(:child => Child.new). In the edit action do @parent = Parent.find(params[:id]) then @parent.child = Child.new unless @parent.child.present?. Something like that.Grison
B
13

As of Rails 3, alias_method is not the preferred way to override association methods. Quoting from the docs:

Overriding generated methods

Association methods are generated in a module that is included into the model class, which allows you to easily override with your own methods and call the original generated method with super. For example:

class Car < ActiveRecord::Base
  belongs_to :owner
  belongs_to :old_owner
  def owner=(new_owner)
    self.old_owner = self.owner
    super
  end
end

If your model class is Project, the module is named Project::GeneratedFeatureMethods. The GeneratedFeatureMethods module is included in the model class immediately after the (anonymous) generated attributes methods module, meaning an association will override the methods for an attribute with the same name.

Bomke answered 21/11, 2013 at 21:42 Comment(1)
Ha ha, awesome! Randomly stumbling upon a friend's answer is like some kind of fate thing.Enesco
P
10

You can alias the original method, then define a new one with the same name.

For example:

class MyModel < ActiveRecord::Base
  has_one :associated_model

  alias :old_associated_model :associated_model

  def associated_model
    old_associated_model || AssociatedModel.new(my_model_id: id)
  end

end

I don't know if this is the canonical way to handle this situation, but it should work.

Pyne answered 24/10, 2012 at 18:14 Comment(1)
This will certainly work. I'm hoping to use the documented way to extend associations, but this is my fallback plan.Technique

© 2022 - 2024 — McMap. All rights reserved.