我想在rails中创建一个与数据库中的表无关的模型。相反,模型应该动态地提取有关其他模型的聚合数据。
示例:
我有一个餐厅模型存储在DB的餐厅表中。我希望有一个RestaurantStats模型,我可以在其上运行一个RestaurantStats.find_total_visitors,或者是RestaurantStats.find_time_spent等......它会返回一组RestaurantStats模型,每个模型都有:
[:restaurant_id,:stat_value]
显然,在每个find ...方法中,stat_value意味着不同的东西(对于find_time_spent,它将花费几秒钟,对于find_total_visitors,它将是访问者的数量)。我们的想法是按时间花费或总访客量返回前100家餐厅。
到目前为止我正在创建一个模型(不是从ActiveRecord继承)
class RestaurantStats
attr_reader :restaurant_id
attr_reader :stat_value
def self.find_total_visitors ...
def self.find_time_spent ...
end
问题是如何以rails y方式定义find_total_visitors,find_time_spent函数,以便填充restaurant_id,stat_value字段?
答案 0 :(得分:2)
你确定你不只是希望这些是餐馆的方法吗?
class Restaurant < ActiveRecord::Base
has_many :visitors
def total_visitors
visitors.count # Or whatever
end
def time_spent
visitors.average(:visit_time) # Or whatever
end
end
答案 1 :(得分:1)
使用self。(fieldname)设置值,然后保存这些值(在运行查找或构建之后)。
答案 2 :(得分:1)
鉴于您希望对统计信息进行排序,向餐厅模型添加计数器缓存似乎是您的最佳选择。
在总访问者的情况下,这很简单。在总time_spent的情况下,它会稍微复杂一点,但仍然无法解决。如果你正在寻找一个平均值,那么事情就会变得复杂一些。
以下是向餐馆模型添加计数器缓存所需的代码。请注意,大多数新模型代码都在访问者的模型中。
通过迁移向Restaurant
添加新列:
class AddCounterCaches < ActiveRecord::Migration
def self.up
add_column :restaurants, :visitors_count, :integer, :default => 0
add_column :restaurants, :total_time_spent, :integer, :default => 0
Restaurant.reset_column_information
Restaurant.find(:all).each do |r|
count = r.visitors.length
total = r.visitors.inject(0) {|sum, v| sum + v.time_spent}
average = count == 0 ? 0 : total/count
r.update_counters r.id, :visitors_count => count
:total_time_spent => total, :average_time_spent => average
end
end
def self.down
remove_column :restaurants, :visitors_count
remove_column :restaurants, :total_time_spent
end
end
更新访客模型以更新计数器缓存
class Vistor < ActiveRecord::Base
belongs_to :restaurant, :counter_cache => true
after_save :update_restaurant_time_spent,
:if => Proc.new {|v| v.changed.include?("time_spent")}
def :update_restaurant_time_spent
difference = changes["time_spent"].last - changes["time_spent"].first
Restaurant.update_counters(restaurant_id, :total_time_spent => difference)
restaurant.reload
avg = restaurant.visitors_count == 0 ?
0 : restaurant.total_time_spent / restaurant.visitors_count
restaurant.update_attribute(:average_time_spent, avg)
end
end
N.B。代码尚未经过测试,因此可能包含轻微错误。
现在,您可以按这些列进行排序,创建包含它们的命名范围或在方法中使用它们。