Martin B.
Martin B.

Reputation: 800

ActiveRecord attribute depends of a calculation of other model

This is my scenario:

class User < ActiveRecord::Base
  has_many :things
  # attr_accessible :average_rating
end

class Thing < ActiveRecord::Base
  belongs_to :user 

  has_one :thing_rating
end

class ThingRating < ActiveRecord::Base
  belongs_to :thing

  attr_accessible :rating
end

I want to have an attribute in my User model which has the average calculation of his related ThingsRating.

What would be the best practice to manage this?

Thanks

Upvotes: 3

Views: 259

Answers (2)

Amar
Amar

Reputation: 6942

May be you can use relation not sure but you can try this

class User < ActiveRecord::Base
  has_many :things
  has_many :thing_ratings, through: :things

  # attr_accessible :average_rating

  def avg_rating
    @avg_rating ||= thing_ratings.average("thing_ratings.rating")
  end 
end

Upvotes: 2

Damien MATHIEU
Damien MATHIEU

Reputation: 32627

The easy way :

class User < ActiveRecord::Base
  has_many :things

  def avg_rating
    @avg_rating ||= average(things.map(&:thing_rating))
  end

  private
  def average(values)
    values.inject(0.0) { |sum, el| sum + el } / arr.size
  end
end

This is fine as a starter. But if you have a bit of trafic, you might find yourself with scaling problems.

You'll then have to refactor this to avoid making an SQL query to the things every time you call the method for a different user.
You could then have several possibilites :

  • Add a field in your User database, avg_rating, which would be updated by the ThingRating when it's created or updated.
  • Use a memcached or redis database to cache the value and invalidate the cache every time a ThingRating is updated or created.

These solutions aren't exhaustive of course. And you could find other ones which would better fit your needs.

Upvotes: 2

Related Questions