2009-01-17 32 views
1

假设Rails应用程序有以下型号的歌曲,播放,radio_station: 歌曲的has_many扮演 发挥的has_many radio_stations radio_station具有属性“CALL_SIGN”如何创建在相关模型运行的取景条件

我想创建一个查找歌曲,将返回我在哪里,最后播放(歌曲)由“WKRP”

的电台播放的所有歌曲,我可以做

发现= [] 歌。找到(:所有,:包括=> [{: plays => [:radio_station]}])。每个do | song | 发现< <歌曲如果song.plays.last.radio_station.call_sign ==“WKRP” 结束

但是这将意味着所有的歌曲必须从DB首先被拉低,并通过...这将得到循环缓慢的歌曲数量和播放时间。

我怎样才能把它放到一个查找条件?

好像这是一件应该是可行的 - 但我不知道我是怎么和我不是一个真正的SQL大师...

然后..会喜欢它挤入一个名为范围,所以我可以把它想:

Song.find_all_last_played_at(“WKRP”)

回答

0

你或许应该看看命名范围。

class Song 
    named_scope :last_played_by, lambda {|call_sign| {:conditions => ["plays.radio_stations.call_sign = ?", call_sign]}} 
end 

你会用它作为这样

Song.last_played_by("WKRZ") 

但是,我意识到,我不知道你正在使用,以找到最后发挥什么样的机制。但是,你可以把它分解成两个命名范围为这样:

class Song 
    named_scope :played_by, lambda {|call_sign| {:conditions => ["plays.radio_station.call_sign = ?", call_sign]}} 
    named_scope :last_play {:conditions => ["MAX(plays.created_at)"] 
end 

要使用它:

Song.last_play.played_by("WKRZ") 

我相信这是正确的,但我没有测试出来,我不所有熟悉命名范围的人。

1

这可能不能由ActiveRecord发现者完成。你必须用滚子查询一些自定义的SQL(MySQL的5必填项):通过使用find_by_sql


SELECT s.* FROM songs s LEFT JOIN plays p 
WHERE p.radio_station_id = [id of WKRP] 
    AND p.created_at = (SELECT MAX(created_at) FROM plays p2 
         WHERE p2.song_id = s.id) 

加载这些歌曲:


Song.find_by_sql('SELECT ...') 

你可以使用一个named_scope,如果你想重构它。



class Song < ActiveRecord::Base 
    has_many :plays 

    named_scope :last_played_on, lambda { |radio_station| 
    { :conditions => ['plays.created_at = (SELECT MAX(p2.created_at) FROM plays p2 
     WHERE p2.song_id = songs.id) AND plays.radio_station_id = ?', radio_station.id], 
     :joins => [:plays] } 
    } 
end 

@wkrp = RadioStation.find_by_name('WKRP') 
Song.last_played_on(@wkrp).each { |song| ... } 

请注意,使用子查询可能会很慢。您可能需要缓存歌曲的最新play_id作为songs表中的字段,以使查询更加轻松快捷。首先在歌曲表中添加一个last_play_id字段。


class Play < ActiveRecord::Base 
    belongs_to :song 

    after_create do |play| 
    play.song.last_play_id = play.id 
    play.song.save! 
    end 
end 
+0

谢谢 - 我将如何做你最后的建议?最新的play_id的缓存?这是否意味着它不会保存到数据库中,而是在每条记录中都可用? – Streamline 2009-01-17 08:32:10

0

对于性能,你应该做wvangergen的建议。但对于语法糖,我想这可能工作:

class Song 
    has_many :plays, :order => 'created_at' do 
    def last_played 
     self.maximum(:created-at) 
    end 
    end 
end 

class Play 
    named_scope :by, lambda {|call_sign| {:conditions => ["radio_stations.call_sign = ?", call_sign]}} 
end 

要使用:

Song.plays.last_played.by("WKRZ") 

我没有测试出来的代码,所以道歉的语法错误=)现在我读它,我不认为它会起作用,但我希望你能弄清楚什么或什么。

0

我认为你可以做到这一点开始与你的关联(注意,默认的“秩序”是“created_at”):

class Song 
    has_many :plays 
    has_many :radio_staions, :through => :plays, :uniq => true 
    has_one :last_radio_station, :through => :plays, :source => :last_radio_station 

    named_scope :last_played_at, lambda {|call_sign| {:include => :last_radio_station, :conditions => ["radio_stations.call_sign = ?", call_sign]}} 
end 

这种预期的播放模式有:

class Play 
    has_one :last_radio_station, :class_name => 'RadioStation', :order => 'created_at' 
    has_many :radio_stations 

那么你可以简单地打电话:

Song.last_played_at("WKRZ") 

我还没有尝试过这个我自己,但希望它会工作。

0

创建了一个named_scope,它为该特定广播电台提供所有播放,命令为'created_at DESC',然后将链接.last放到返回查询的末尾。沿线:

Song.played_by('WRXD').last