Tengo un modelo llamado Song
. También tengo un modelo llamado Listen
. A Listen
belongs_to :song
, y una canción :has_many listens
(se puede escuchar muchas veces).
En mi modelo, quiero definir un método self.top
que debería devolver las 5 canciones más escuchadas. ¿Cómo puedo lograr eso usando la relación has_many
?
Estoy usando Rails 3.1.
¡Gracias!
Usar ámbitos con nombre :
class Song has_many :listens scope :top5, select("songs.id, OTHER_ATTRS_YOU_NEED, count(listens.id) AS listens_count"). joins(:listens). group("songs.id"). order("listens_count DESC"). limit(5) Song.top5 # top 5 most listened songs
Mejor aún, use counter_cache
que será más rápido porque solo lo hará porque usa una tabla en su consulta
Aquí está tu clase de canción:
class Song < ActiveRecord::Base has_many :listens def self.top order('listens_count DESC').limit(5) end end
Entonces, tu clase de escucha:
class Listen < ActiveRecord::Base belongs_to :song, counter_cache: true end
Asegúrese de agregar una migración:
add_column :comments, :likes_count, :integer, default: 0
Puntos de bonificación, agregar prueba:
describe '.top' do it 'shows most listened songs first' do song_one = create(:song) song_three = create(:song, listens_count: 3) song_two = create(:song, listens_count: 2) popular_songs = Song.top expect(popular_songs).to eq [song_three, song_two, song_one] end end
O bien, si desea utilizar el método anterior, aquí es un poco más simple y utiliza un método de clase en lugar de un scope
def self.top select('comments.*, COUNT(listens.id) AS listens_count'). joins(:listens). group('comments.id'). order('listens_count DESC'). limit(5) end
Para los Rails 4.x intente esto si sus filas sin ninguna asociación son importantes:
scope :order_by_my_association, lambda { select('comments.*, COUNT(listens.id) AS listens_total') .joins("LEFT OUTER JOIN listens ON listens.comment_id = comments.id") .group('comments.id') .order("listens_total DESC") }