Значение даты кэширования ассоциации ActiveRecord в предложении условий

#ruby-on-rails

Вопрос:

Моя Account модель имеет следующие две ассоциации:

 has_many  :expenses, 
          :order => 'expenses.dated_on DESC', 
          :dependent => :destroy

has_many  :recent_expenses, 
          :class_name => 'Expense', 
          :conditions => "expenses.dated_on <= '#{Date.today}'",
          :order => 'dated_on DESC', 
          :limit => 5
 

В одном из моих представлений я показываю недавние расходы примерно так:

 <% @account.recent_expenses.each do |expense| %>
...
<% end %>
 

На моей машине разработки, на промежуточном сервере (который работает в рабочем режиме), а также на рабочей консоли @account.recent_expenses возвращает правильный список. Однако на нашем рабочем сервере самые последние расходы не возвращаются.

Если я заменю @account.recent_expenses на @account.expenses в представлении, отобразятся самые последние расходы, поэтому я предполагаю, что #{Date.today} часть условия каким-то образом кэшируется при первом выполнении. Если я перезапущу производственный кластер беспородных, все последние расходы будут возвращены правильно.

Может ли кто-нибудь подумать, почему это может произойти и как я могу изменить :recent_expenses запрос, чтобы этого не произошло?

Я использую Rails 2.1.0.

Ответ №1:

Rails создает запрос при загрузке, а затем будет повторно использовать этот запрос каждый раз, когда вы вызываете @account.recent_expenses, что именно то, что вы испытываете.

Если вы используете Rails 2.1, вы можете использовать named_scope для достижения того, что вы ищете.

в вашей модели расходов укажите следующее:

 named_scope :recent, lambda { {:conditions => ["expenses.dated_on <= ?", Date.today], :order => 'dated_on DESC', :limit => 5 } }
 

Лямбда-код используется для обеспечения того, чтобы rails перестраивал запрос каждый раз.

из вашей учетной записи удалить:

 has_many :recent_expenses ...
 

а потом позвони:

 <% @account.expenses.recent.each do |expense| %>
...
<% end %>
 

Ответ №2:

Как сказал Эндрю, макросы ассоциации считываются при запуске приложения, поэтому любые динамические биты (например, Date.today) анализируются в этот момент. Его решение работает, если вы работаете на Rails 2.1 или более поздней версии; для более ранних версий вы можете использовать драгоценный камень has_finder или просто создать следующий метод в модели расходов:

 def self.recent
  find(:all, :conditions => ['dated_on <= ?', Date.today], :limit => 5, :order => 'dated_on DESC')
end
 

Методы класса, подобные этому, подвержены ассоциациям и имеют правильную область действия — разница между ними и named_scopes заключается в том, что вы не можете связать их в цепочку.

Ответ №3:

Вот как это будет выглядеть в Rails 3:

 scope :recent, lambda { where("expenses.dated_on <= ?", Date.today).order('dated_on DESC').limit(5) }
 

ВНИМАНИЕ: Следите за прикованными областями, которые не завернуты в лямбду. Вы можете подумать, что часть цепочки оценивается во время выполнения, но она может быть оценена при запуске сервера приложений. Убедитесь, что вы лямбда-обертываете любую область, которая будет использовать эти другие области. Объяснение здесь:
http://www.slashdotdash.net/2010/09/25/rails-3-scopes-with-chaining/