简体   繁体   中英

Ruby on Rails - Sum calculation not working with associations in model

I have a User table with a column total_user_xp with a has_many :goals association.

The Goal table has a column total_goal_xp which is calculated in the model with the following method:

# goal.rb

  belongs_to :user
  has_many :goal_activities
  has_many :activities, through: :goal_activities

def total_goal_xp
    self.total_goal_xp = self.goal_activities.sum(:total_xp)
end 

For some reason, in my controller and model file for User, I can't calculate the sum for total_goal_xp where the user_id matches the current_user. It always returns the value 0 as if the total_goal_xp wasn't being stored in the table.

I'm fairly new to rails but I have a feeling my total_goal_xp method isn't saving the value to the db. It seems to recalculate the sum every time that method is called. That said, in the rails console, running Goal.all gives me a table which does have the correct values so I don't understand why the following methods return the value 0.

user_controller.rb

def show
    @user_goals = current_user.goals.all
    @user_xp = @user_goals.sum(:total_goal_xp)
end

user.rb

has_many :goals

def set_total_user_xp
  goal = self.goals.all
  self.total_user_xp = goal.sum(:total_goal_xp)
end

In the server log, I get the following SQL - SELECT SUM("goals"."total_goal_xp") FROM "goals" WHERE "goals"."user_id" = ? [["user_id", 1]] SELECT SUM("goals"."total_goal_xp") FROM "goals" WHERE "goals"."user_id" = ? [["user_id", 1]] for both methods.

I know I shouldn't be doing the calculations in the controller but at the moment, I just want to get the calculation to work so I know the associations are working.

Would appreciate any advice on the best practice for saving the total_goal_xp and the doing the same for the total_user_xp.

Let me know if you'd like to see any other files.

Firstly, you have defined method name same as attribute/column name, that's a conflict. Secondly, if you want to calculate the total on the go, you don't need those columns.

One way I would solve this as the columns are present, set the total value when the values are changed using callback mentioned in this answer

def set_total_xp
  goal_activity = self.goal_activities.where(goal_id: goal_id).first_or_initialize
  goal_activity.update_attribute(:total_xp, (quantity*goal.xp))
  goal = Goal.find_by_id(goal_id)
  user = goal.user
  goal.update_attribute(:total_goal_xp, goal.goal_activities.sum(:total_xp))
  user.update_attribute(:total_user_xp, user.goals.sum(:total_goal_xp))
end

OR you don't even need to recalculate sum everytime

def set_total_xp
  goal_activity = self.goal_activities.where(goal_id: goal_id).first_or_initialize
  goal_activity.update_attribute(:total_xp, (quantity*goal.xp))
  goal = Goal.find_by_id(goal_id)
  xp = goal_activity.total_xp
  goal.update_attribute(:total_goal_xp, (goal.total_goal_xp + xp))
  goal.user.update_attribute(:total_user_xp, (user.total_user_xp + xp))
end

Now you don't need those methods and also, when displaying the data, there's no query hit to the db..

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM