开发者

Avoiding individual database calls for count

开发者 https://www.devze.com 2023-02-18 17:03 出处:网络
My models look like this: class Movie < ActiveRecord::Base attr_accessible :title, :year, :rotten_id, :audience_score,

My models look like this:

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

When I'm displaying my index of Actors, I'd like to show the number of movies each actor has starred in. I can do this with @actor.movies.count, however this generates an SQL query for each actor. With, say, 30 actors, this will result in 30 extra queries in addition to the initial.

Is开发者_JAVA百科 there any way to include the count of movies each actor has participated in, in the initial Actor.all call? And thereby getting things done with only one call. Extra bonus if this was sorted by said count.

Update: All answers provided has been helpful, and though it turned into some dirt-slinging-contest at some point, it worked out well. I did a mish-mash of all your suggestions. I added a movies_counter column to my Actor model. In my Job model I added belongs_to :actor, :counter_cache => :movies_counter. This works brilliantly, and is automatically updated when i create or destroy a movie, without me adding any further code.


As @Sam noticed, you should add new column to actors table movies_counter

rails g migration add_movies_counter_to_actor movies_counter:integer

Now you can edit your migration

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

And run it

rake db:migrate

Then you should add two callbacks: after_save and 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

Then you can call some_actor.movies_counter


Add a column to your Actor table called 'movie_count'. Then add a call back in your Actor model that updates that column.

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

That way your just have an integer that gets updated instead of calling all records.

0

精彩评论

暂无评论...
验证码 换一张
取 消