假设rails app有以下型号的歌曲,播放,radio_station: 歌曲has_many播放 玩has_many radio_stations radio_station具有属性“call_sign”
我想为Song创建一个发现,它将为我返回所有歌曲,其中最后一个(歌曲的)播放由“WKRP”广播电台播放
我能做到
found = [] Song.find(:all,:include => [{:plays => [:radio_station]}])。每个歌曲|歌曲| 发现<<歌曲如歌曲.plays.last.radio_station.call_sign ==“WKRP” 端
但这意味着所有的歌曲必须首先从数据库中下载并循环播放......随着歌曲和剧本的构建,这些歌曲会变慢。
如何将其置于一个查找条件中?
看起来这应该是可行的 - 但我无法弄清楚我是怎样的,我不是一个真正的SQL大师......
然后..会喜欢把它挤进一个命名的范围,所以我可以称之为:
Song.find_all_last_played_at( “WKRP”)
答案 0 :(得分:1)
这可能是ActiveRecord查找器无法完成的。您必须使用子查询(需要MySQL 5)滚动一些自定义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)
使用find_by_sql
:
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
答案 1 :(得分: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")
我认为这是正确的,但我没有测试过,我对命名范围并不熟悉。
答案 2 :(得分: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")
我没有测试代码,所以对语法错误道歉=)现在我正在阅读它,我认为它不会起作用,但我希望你能够弄明白或某事
答案 3 :(得分:0)
我认为你可以从关联开始做到这一点(注意默认的'order'是'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
这需要您的Play模型:
class Play
has_one :last_radio_station, :class_name => 'RadioStation', :order => 'created_at'
has_many :radio_stations
然后你可以简单地打电话:
Song.last_played_at("WKRZ")
我自己没有尝试过,但希望它会起作用。
答案 4 :(得分:0)
创建一个named_scope,用于拉出该特定无线电台的所有播放,命令'created_at DESC',然后链.last到返回查询的末尾。沿着:
Song.played_by('WRXD').last