Как предположил Кевин, counter_cache - самый простой вариант, определенно то, что я бы использовал.
class Author < ActiveRecord::Base
has_many :books, :counter_cache => true
end
class Book < ActiveRecord::Base
belongs_to :author
end
И если вы используете Rails 2.3 и хотите, чтобы это был порядок по умолчанию, вы можете использовать новый метод default_scope:
class Author < ActiveRecord::Base
has_many :books, :counter_cache => true
default_scope :order => "books_count DESC"
end
books_count - это поле, которое выполняет поведение кэширования счетчика, и, вероятно, есть лучший способ, чем использовать его непосредственно в области действия по умолчанию, но оно дает вам идею и выполнит работу.
EDIT:
В ответ на комментарий, спрашивающий, будет ли counter_cache работать, если приложение, не относящееся к rails, изменяет данные, это вполне возможно, но не по умолчанию, так как Rails увеличивает и уменьшает счетчик во время сохранения. Что вы можете сделать, это написать свою собственную реализацию в обратном вызове after_save.
class Author < ActiveRecord::Base
has_many :books
after_save :update_counter_cache
private
def update_counter_cache
update_attribute(:books_count, self.books.length) unless self.books.length == self.books_count
end
end
Теперь у вас не установлен counter_cache, но если вы назовете поле в базе данных books_count в соответствии с соглашением counter_cache, то при поиске:
@Author = Author.find(1)
puts @author.books.size
Он будет по-прежнему использовать счетный номер счетчика вместо выполнения поиска в базе данных. Конечно, это будет работать только тогда, когда приложение rails обновит таблицу, поэтому, если другое приложение что-то делает, ваши цифры могут быть не синхронизированы, пока приложение rails не вернется и не сохранит данные. Единственный способ обойти это, что я могу придумать, - это использовать cron для синхронизации чисел, если ваше приложение rails не выполняет поиск достаточно часто, чтобы это не имело значения.