Topic: Reload/reinitialize a model association?

Is there a way to reload or reinitialize a model association once an application is running?

class Project < ActiveRecord::Base

  has_many :milestones,   
           :conditions => ["milestone_status_id = ?", MilestoneStatus.find_by_name("Active")]

end


It appears that Rails will define the association while the application is initializing, and then continue to use that definition until the application exits.  This will cause an undesired association if the value "Active" cannot be found in the associated model.  I tripped across this behavior during a migration.  A run through the log revealed that Rails has defined the association as "milestone_status_id = nil" because the entry for "Active" had not been added to the database while the migration process was initializing.  The plan, of course, is to add "Active" to the table during the migration, but by then it is too late.

Furthermore, the :conditions portion of this association will throw "table not found" errors if attempting to rebuild the database via migrations.  The rake db:migrate process will exit during environment initialization.

The knee-jerk solution is to use "MilestoneStatus.find_or_create_by_name("Active").  While that would solve the erroneous "milestone_status_id = nil" definition, it doesn't address the "table not found" error when migrating and the MilestoneStatus table hasn't been created yet.

Here's a brutish version of the association that handles the "table not found" error, but it also highlights the need to reload/reinitialize the association:

class Project < ActiveRecord::Base

  has_many :milestones,   
           :conditions => ["milestone_status_id = ?", MilestoneStatus.table_exists? ? MilestoneStatus.find_or_create_by_name("Active") : 0]

end


If the MilestoneStatus table has been created, Rails adds/finds "Active" to the table and generates the correct association definition.  Otherwise the :conditions line is defined as "milestone_status_id = 0" (which should never return a matching record).

I know the script/console has the handy reload! command, but that seems to have a bug correctly processing includes from the lib folder.  And it's also not available from inside a running application or migration.

Of course, I could be going about this all wrong.  If that's the case, please tell me!  smile

edits: cleaned up association examples to remove superfluous code

Last edited by GG Crew (2008-07-28 13:45:05)

Re: Reload/reinitialize a model association?

Interestingly, this issue appears to have resolved itself.  The associations now work correctly, even when migrating from an empty database.

I will test further, both in Windows and OS X, and attempt to recreate the issue.  For what it's worth, the issue occured while I was running the app in server mode in one process while executing the rake migration in another process.

Last edited by GG Crew (2008-08-09 19:31:54)

Re: Reload/reinitialize a model association?

Sorry to dig up an old post, but I tripped across my very own (unanswered) question while searching for a solution to a similar situation.

Like my original post a few years ago, an association was tripping up my from-scratch migrations because the association was referencing a record that had not been created when the migration began.

I have found a decent-enough solution: redefine the model as part of the migration.

Using my old code, here's an example of a model and migration that will generate an error when running a full migration on an empty database:

class Project < ActiveRecord::Base

  has_many :milestones,   
           :conditions => ["milestone_status_id = ?", MilestoneStatus.find_by_name("Active")]

end
class CreateDestroyProject < ActiveRecord::Migration

  Project.create!(:name => 'test')  # record is created
  Project.first.destroy  # migration crashes because :milestones association is referencing a nil value

end

The problem will still occur if you add the needed data earlier in the migration.  And it doesn't matter if the data is added in an earlier migration file - if the data wasn't there when "rake db:migrate" was called, the project.milestones association will refer to a nil value.

Using the same Project class that was defined earlier in this post, here's the updated migration code that redefines the Project model:

class CreateDestroyProject < ActiveRecord::Migration

  class Project < ActiveRecord::Base; end

  Project.create!(:name => 'test')  # record is created
  Project.first.destroy  # success -- no funky association to crash our migration

end

Redefining the model during a migration will override any associations, validations, constants, or custom methods that you've added to the model's definition.  Actually, redefining the model at any point during an application's run will override all the wonderfullness that you've added to the model's definition.  This would be disastrous if used in your main application code.  But in a migration, it can be handy and does not affect the model when in development or production.

Some of you might balk at this "solution" - understandably so!  This is a "use with caution" technique.  There might be later migrations that use some of the model's extra methods, and the migration will crash because those methods will be undefined. (eg "project.milestones.create!(:name => 'milestone test')")  But the Rails folks discourage using associations during migrations for this very reason.  They acknowledge that from-scratch migrations can fail because models/associations may have changed since the failing migration was written.

Anyhow - I hope this helps someone besides myself!

Re: Reload/reinitialize a model association?

To add to this, migrations are not ideal for inserting or updating data. If you need to have some sort of default data, such as an admin user, add it to db/seeds.rb file using the active record methods, like Model.create!(). The idea is when you deploy, you run your migrations, then you can run the rake db:seed command if necessary. Your migrations shouldn't depend on data that's not there. Migrations are purely meant to setup the data model of the application.

Last edited by agm_ultimatex (2011-10-15 23:27:14)

Re: Reload/reinitialize a model association?

Sorry I have a questions.  How i can create new empty active Record result in Rails 3.0? So then i have to create new record because i have to insert value from redis db.