David Carney
David Carney

Reputation: 2208

Problem with stale ActiveRecord association in Rails 3

I have three models: Site, Subscription, Plan:

Site

has_one     :subscription
has_one     :plan, :through => :subscription

Subscription

belongs_to  :site
belongs_to  :plan

Plan

has_many  :subscriptions

In my SubscriptionsController I'm handing a POST by first loading the relevant objects:

@site = current_user.site
@subscription = @site.subscription
@plan = @subscription.plan

Unfortunately, if @subscription.plan is modified and saved before the POST then that change is not reflected in @site unless I perform a @site.reload at the beginning of my controller method.

ex.

eval @subscription
> #<Subscription id: 6115, site_id: 5634, plan_id: 342, ... <== GOOD

eval @site.plan
> #<Plan id: 337, ... <== BAD

eval @subscription.site.plan
> #<Plan id: 342, ... <=== GOOD

eval @site.reload
eval @site.plan
> #<Plan id: 342,... <== GOOD

Any ideas?

UPDATE

There are two viable answers below, but I'm going to hold off on accepting either of them until I learn more about the 'touch' option that Jesse mentioned (which, unfortunately, doesn't work for me....yet).

Upvotes: 0

Views: 1332

Answers (2)

Jesse Wolgamott
Jesse Wolgamott

Reputation: 40277

You may find success with the :touch method on your relations.

The general idea is that if I update a comment, the posts's updated_at will get updated as well. I think this will translate to your @site record in memory.

class Site
  has_one     :subscription, :touch=>true
  has_one     :plan, :through => :subscription, :touch=>true

class Subscription

  belongs_to  :site, :touch=>true
  belongs_to  :plan, :touch=>true

When you update your site.subscription.plan, this will update the subscription and the plan.

More information: http://ryandaigle.com/articles/2009/4/20/what-s-new-in-edge-rails-touching

Upvotes: 1

Srdjan Pejic
Srdjan Pejic

Reputation: 8202

It's just how Active Record is designed. It tries not to hit your database too often and will prefer to read from memory than the database.

Preferably, you should be modifying the Plan through the Site rather than separately from it. Basically, if you're reading by going through the @site, you should be writing through @site, as well.

If that's not possible or desirable, you may want to have a small helper method checking the updated_at column to see if the record has changed and reloading Site if it did. If it sounds hacky, that's because it is. :)

Upvotes: 0

Related Questions