表列自动计算平均评级

时间:2013-10-07 01:13:12

标签: ruby-on-rails average rating

我正在关注tutorial并拥有模型userhotelrating。用户可以创建酒店,用户可以对其进行评级。用户评分值与ratinguser_id一起记录到表格hotel_id。当我呈现部分<%= render "hotels/hotels_list", :@hotels => Hotel.all %>时,它会显示在模型hotel中计算的平均评分的酒店列表 Model Hotel.rb:

class Hotel < ActiveRecord::Base
  attr_accessible :user_id
  belongs_to :user
  has_many :ratings
  has_many :raters, :through => :ratings, :source => :users

  def average_rating
    @value = 0
    self.ratings.each do |rating|
      @value = @value + rating.value
    end
    @total = self.ratings.size
    '%.2f' % (@value.to_f / @total.to_f)
  end
end

Model User.rb:

class User < ActiveRecord::Base
  has_many :hotels
  has_many :ratings
  has_many :rated_hotels, :through => :ratings, :source => :hotels
end

型号Rating.rb:

class Rating < ActiveRecord::Base
  attr_accessible :value
  belongs_to :user
  belongs_to :hotel
end

我需要按平均评分对酒店列表进行排序,可能需要添加一些列average_rating,它会立即像酒店模型中的average_rating方法一样计算平均值,所以我可以很容易访问它。我该如何解决这个问题? 的 RatingsController.rb

class RatingsController < ApplicationController

      before_filter :authenticate_user!
      def create
        @hotel = Hotel.find_by_id(params[:hotel_id])
        @rating = Rating.new(params[:rating])
        @rating.hotel_id = @hotel.id
        @rating.user_id = current_user.id
        if @rating.save
          respond_to do |format|
            format.html { redirect_to hotel_path(@hotel), :notice => "Your rating has been saved" }
            format.js
          end
        end
      end

      def update
        @hotel = Hotel.find_by_id(params[:hotel_id])
        @rating = current_user.ratings.find_by_hotel_id(@hotel.id)
        if @rating.update_attributes(params[:rating])
          respond_to do |format|
            format.html { redirect_to hotel_path(@hotel), :notice => "Your rating has been updated" }
            format.js
          end
        end
      end 
    end

2 个答案:

答案 0 :(得分:2)

很简单。首先,您可以通过迁移将average_rating列添加到酒店模型中。然后,您将向评级模型添加回调,以更新酒店模型中的值。基本上,每次创建,销毁或更新评级时,您都需要更新平均评级。它看起来像这样:

class Hotel < ActiveRecord::Base
  [ code snipped ]

  def update_average_rating
    @value = 0
    self.ratings.each do |rating|
      @value = @value + rating.value
    end
    @total = self.ratings.size


    update_attributes(average_rating: @value.to_f / @total.to_f)
  end
end

class Rating
  belongs_to :hotel
  after_create :update_hotel_rating

  def update_hotel_rating
    hotel.update_average_rating
  end
end

现在您可以轻松按评分排序。我要留下一些细节,但我想你可以在这里得到一般的想法。

答案 1 :(得分:0)

在@muffinista的例子中,你应该做的更多&#39; Ruby-ish&#39;并在一行中完成:

def update_average_rating
  update_attributes(average_rating: self.ratings.collect(&:value).avg)
end

如果您希望nil,则可以.compact.avg

您需要使用#avg扩展Array:

class Array
  def avg
    sum.to_f/size
  end
end