kwyoung11
kwyoung11

Reputation: 978

How to use after_save callback in child model to update attribute in parent model?

I have two models, User and TrainingSession:

class User < ActiveRecord::Base
has_many :training_sessions
end

class TrainingSession < ActiveRecord::Base
belongs_to :user
end

Each User has a certain number of points based on the sum of the durations of their accumulated training_sessions (in TrainingSession model):

class TrainingSession
def points
  sum(:duration) / 6
end

Currently, after a user creates a new training_session, I am updating the points attribute in the User model as follows (from TrainingSession controller):

# POST /training_sessions
# POST /training_sessions.json
def create
@user = User.find_by_id(session[:user_id])
@training_session = @user.training_sessions.new(params[:training_session])
@points = @user.training_sessions.points
@user.update_attribute(:points, @points)
...

The problem is that each time a training_session is created, it doesn't include the points that were just accrued by the user from the most recently created training_session. For example, if a user has 160 points, then they go for a 60 minute run, they will have gained 10 points to reach 170 points. But after creating that 60 minute run training_session, the User model updates it to 160 points. So it is always one step behind, so to speak. I tried using an after_save callback on the TrainingSession model, like so:

class TrainingSession < ActiveRecord::Base
after_save :update_points  

private

def update_points
 count = @user.training_sessions.sum(:duration)
 if count >= 1
  points = count / 6
 end
 @user.update_attribute(:points, points)
end

but then I get this error when trying to create a new training_session:

undefined method `training_sessions' for nil:NilClass

So, my question is, how do I keep up to date the :points attribute on my user model with the inclusion of the most recently created training_session?

Upvotes: 2

Views: 1458

Answers (1)

Mischa
Mischa

Reputation: 43298

@user is undefined in your model. That's why you get the error.

A training session belongs to a user, so in your model you can get the user via self.user or simply user. This code should work:

def update_points
  count = self.user.training_sessions.sum(:duration)
  points = count / 6 if count >= 1
  self.user.update_attribute(:points, points)
end

Upvotes: 3

Related Questions