Избегание отдельных вызовов базы данных для подсчета
-
27-10-2019 - |
Вопрос
Мои модели выглядят так:
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
вызов? И тем самым все делало все с одним звонком. Дополнительный бонус, если это было отсортировано по указанию.
Обновлять:Все предоставленные ответы были полезными, и хотя в какой-то момент это превратилось в какой-то грязь-сплинг-контейнер, это сработало хорошо. Я сделал из всех ваших предложений. Я добавил столбец Movie_counter в мою модель актера. В своей модели работы я добавил belongs_to :actor, :counter_cache => :movies_counter
. Анкет Это работает блестяще и автоматически обновляется, когда я создаю или уничтожаю фильм, не добавляя дальнейшего кода.
Решение
Как заметил @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
Другие советы
Добавьте столбец в таблицу актера под названием «Movie_Count». Затем добавьте звонок в модель актера, которая обновляет этот столбец.
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
Таким образом, у вас просто есть целое число, которое обновляется вместо того, чтобы называть все записи.