views:

54

answers:

4

I have a simple has_one/belongs_to relationship between two models.

This is a new association in my application so there are many records that do not yet have the associated record created.

Throughout my application I'm assuming the model has the association and I'm accessing its attributes and methods. However, because the association doesn't exist, I'm running into a lot of errors.

What I would like to do is unobtrusively build the associated record on the fly whenever it's access for the first time through any of its methods and attributes. It does not matter that there is data in record, I simply need it to exist so those methods I'm calling can build the data.

Edit: I do not want to check and create the record on all of the instances where I'm trying to access the relationship, so idealy this needs to be done on the model itself and not in my controllers anywhere.

Any thoughts?

Thanks!

A: 

In the controller, you could put something like this in the show method (untested, but it should give you an idea:

@thing = Thing.find params[:id]
if @thing.other_thing.nil?
  @thing.other_thing = OtherThing.new.save!
  @thing.save!
end

This isn't ideal, and you could probably clean it up a lot by putting a method in the Thing model that would check for and create the related model instead of putting it into your controller.

Another option would be to create a new accessor that you use to access the other_thing, creating it as required.

However, the correct thing to do is probably to fix your data, either in a migration or directly, creating the related models properly.

Tim Sullivan
Correct, this isn't ideal. I would need to do this wherever it's accessed. That's why I need to have it done in the model itself or on the relationship.
mwilliams
I've added a little more detail for alternatives.
Tim Sullivan
A: 

The direct answer is to override method for the relationship. When called it will check if the record exists and create it if it doesn't.

However, I would recommend that you use a migration to create all of the records up front.

jshen
A: 

I have done this type of thing before but not on the model level. Ive done it on the controller level with a before_filter that ran before all methods which needed to access the model association that did or did not exist yet.

I just realized there is the after_find and after_initialize callbacks that you can use in the model.

You could stick:

def after_initialize
   association.build if association.nil?
end

in your model and it should solve your problems.. (disclaimer: untested by me) :)

Corban Brook
+1  A: 

Here's what we ended up with that did the trick. I didn't write it (a co-worker did) but it passes the previously failing tests that I wrote for this case.

def stats_with_create
  stats_without_create || create_stats
end
alias_method_chain :stats, :create
mwilliams