#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/