Избегание отдельных вызовов базы данных для подсчета - PullRequest
1 голос
/ 20 марта 2011

Мои модели выглядят так:

class Movie < ActiveRecord::Base
  attr_accessible :title, :year, :rotten_id, :audience_score,
    :critics_score, :runtime, :synopsis, :link, :image

  has_many :jobs, :dependent => :destroy
  has_many :actors, :through => :jobs
end

class Actor < ActiveRecord::Base
  attr_accessible :name
  has_many :movies, :through => :jobs
  has_many :jobs, :dependent => :destroy
end

class Job < ActiveRecord::Base
  attr_accessible :movie_id, :actor_id

  belongs_to :movie
  belongs_to :actor
end

Когда я показываю свой индекс актеров, я хотел бы показать количество фильмов , в которых каждый актер снялся.Я могу сделать это с @actor.movies.count, однако это генерирует запрос SQL для each actor.С, скажем, 30 актерами, это приведет к 30 дополнительным запросам в дополнение к начальному.

Есть ли способ включить количество фильмов, в которых участвовал каждый актер, в начальный вызов Actor.all?И тем самым добиться цели всего за один звонок.Дополнительный бонус, если это было отсортировано по указанному количеству.

Обновление: Все предоставленные ответы были полезными, и хотя в какой-то момент это превратилось в какой-то грязный бой, все получилось,Я сделал мешанину из всех ваших предложений.Я добавил колонку movies_counter в свою модель актера.В моей модели работы я добавил belongs_to :actor, :counter_cache => :movies_counter.Это прекрасно работает и автоматически обновляется, когда я создаю или уничтожаю фильм, без добавления дополнительного кода.

Ответы [ 2 ]

2 голосов
/ 20 марта 2011

Как заметил @Sam, вы должны добавить новый столбец в actors таблицу movies_counter

rails g migration add_movies_counter_to_actor movies_counter:integer

Теперь вы можете редактировать вашу миграцию

class AddMoviesCounterToActor < ActiveRecord::Migration
  def self.up
    add_column :actors, :movies_counter, :integer, :default => 0

    Actor.reset_column_information
    Actor.all.each do |a|
      a.update_attribute :movies_counter, a.movies.count
    end
  end

  def self.down
    remove_column :actors, :movies_counter
  end
end

И запустить его

rake db:migrate

Затем вы должны добавить два обратных вызова: after_save и after_destroy

class Movie < ActiveRecord::Base
  attr_accessible :title, :year, :rotten_id, :audience_score,
    :critics_score, :runtime, :synopsis, :link, :image

  has_many :jobs, :dependent => :destroy
  has_many :actors, :through => :jobs

  after_save :update_movie_counter
  after_destroy :update_movie_counter

  private
  def update_movie_counter
    self.actors.each do |actor|
      actor.update_attribute(:movie_count, actor.movies.count)
    end
  end
end

Тогда вы можете позвонить some_actor.movies_counter

0 голосов
/ 20 марта 2011

Добавьте столбец в вашу таблицу Actor с именем 'movie_count'. Затем добавьте обратный вызов в модель Actor, которая обновит этот столбец.

class Movie < ActiveRecord::Base
  has_many :actors, :through => :jobs
  before_save :update_movie_count
  def update_movie_count
     self.actor.update_attribute(:movie_count, self.movies.size)
  end
end

Таким образом, у вас просто есть целое число, которое обновляется вместо вызова всех записей.

...