2009-01-17 5 views
1

rails app considéré la chanson modèles suivants, le jeu, radio_station: chanson has_many joue radio_stations jeu has_many radio_station a attribut « CALL_SIGN »Comment créer une condition de chercheur qui fonctionne sur le modèle associé

Je veux créer une trouvaille pour la chanson qui retournerait pour moi les toutes les chansons où le dernier jeu (de la chanson) a été jouée par une station de radio de « WKRP »

que je pouvais faire

trouvées = [] song. find (: all,: include => [{: plays => [: radio_station]}]). chaque do | chanson | trouvé < < chanson si song.plays.last.radio_station.call_sign == « WKRP » fin

mais cela signifierait que toutes les chansons doivent être tiré vers le bas de la DB première et bouclées ... qui se lent comme le nombre de chansons et joue des constructions.

Comment puis-je mettre cela dans une condition de recherche?

Il semble que cela est quelque chose qui devrait être faisable - mais je ne peux pas comprendre comment et je ne suis pas vraiment un gourou SQL ...

puis .. aimerais le presser dans un nom portée pour que je puisse l'appeler comme:

Song.find_all_last_played_at ("WKRP")

Répondre

0

Vous devriez probablement à named scopes.

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

Vous l'utiliser comme tel

Song.last_played_by("WKRZ") 

Cependant, je me suis aperçu que je ne sais pas quel mécanisme vous utilisez pour trouver la dernière pièce. Mais vous pouvez le diviser en deux champs nommés en tant que tel:

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 

Pour l'utiliser:

Song.last_play.played_by("WKRZ") 

Je crois que oui, mais je ne l'ai pas testé dehors et je ne suis pas tout ce qui est familier avec les portées nommées.

1

Cela ne peut probablement pas être effectué par les moteurs de recherche ActiveRecord. Vous devez lancer une SQL personnalisée avec une sous-requête (MySQL 5 requis): en utilisant 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) 

charge ces chansons:


Song.find_by_sql('SELECT ...') 

Vous pouvez factoriser à l'aide d'un named_scope si vous voulez.



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| ... } 

Notez que l'utilisation de sous-requêtes peut être lente. Vous pouvez mettre en cache la dernière play_id d'une chanson en tant que champ dans le tableau songs pour faciliter et accélérer les requêtes. Commencez par ajouter un champ last_play_id à votre tableau de chansons.


class Play < ActiveRecord::Base 
    belongs_to :song 

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

merci - comment est-ce que je ferais votre dernière suggestion? la mise en cache du dernier play_id? cela signifie-t-il qu'il n'est pas enregistré dans la base de données mais disponible dans chaque enregistrement? – Streamline

+0

Ajoutez un last_play_id à la table des chansons. Puis ajoutez du code comme ceci: class Lecture after_create do | play.song.last_play_id = play.id play.song.save! fin fin – wvanbergen

0

Pour la performance, vous devriez probablement faire ce que wvangergen suggère. Mais pour le sucre syntactique, je pense que cela pourrait fonctionner:

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 

Pour utiliser:

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

Je ne suis pas tester le code, donc mes excuses pour les erreurs de syntaxe =) Maintenant que je suis En le lisant, je ne pense pas que ça marchera, mais j'espère que vous pourrez peut-être le comprendre ou quelque chose.

0

Je pense que vous pouvez le faire en commençant par vos associations (Notez que la valeur par défaut 'ordre' est '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 

Cela suppose que votre modèle Play Souhaitée:

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

Alors vous pouvez simplement appeler:

Song.last_played_at("WKRZ") 

Je n'ai pas essayé cela moi-même, mais j'espère que cela fonctionnera.

0

Créé un named_scope qui récupère toutes les lectures pour cette station de radio particulière, avec l'ordre 'created_at DESC', puis chaîne .last à la fin de la requête retournée. Sur le modèle de:

Song.played_by('WRXD').last 
Questions connexes