2008-10-07 6 views
2

Mon modèle Account a les deux associations suivantes:ActiveRecord Association cache valeur Date dans des conditions clause

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 

Dans un de mes vues, je suis rendu des dépenses récentes comme ceci:

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

Sur mon machine de développement, sur le serveur de transfert (qui s'exécute en mode production) et également sur la console de production, @account.recent_expenses renvoie la liste correcte. Cependant, sur notre serveur de production en direct, les dépenses les plus récentes ne sont pas retournées.

Si je remplace @account.recent_expenses avec @account.expenses dans la vue, les plus récentes dépenses sont affichés, donc je suppose que la partie #{Date.today} de la clause de conditions est mise en mémoire cache en quelque sorte la première fois qu'il est exécuté. Si je redémarre le cluster Mongrel de production, toutes les dépenses les plus récentes sont renvoyées correctement.

Quelqu'un peut-il penser à ce qui se produirait et comment puis-je changer la requête :recent_expenses pour éviter que cela se produise? J'utilise Rails 2.1.0.

Répondre

6

Rails construit la requête lorsqu'il est chargé, puis réutilise cette requête chaque fois que vous appelez @ account.recent_expenses, ce qui est exactement ce que vous rencontrez.

Si vous utilisez Rails 2.1, vous pouvez utiliser named_scope pour obtenir ce que vous cherchez.

dans votre frais modèle, mettre les éléments suivants:

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

Le lambda est utilisé pour assurer des rails reconstitue la requête à chaque fois.

de votre compte supprimer:

has_many :recent_expenses ... 

puis appelez:

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

Comme Andrew dit, les macros d'association sont lues au démarrage de l'application, de sorte que tous les bits dynamiques (comme Date.today) sont analysés à ce moment-là. Sa solution fonctionne si vous êtes sur Rails 2.1 ou plus tard; pour les versions antérieures, vous pouvez utiliser le has_finder gem, ou tout simplement créer la méthode suivante sur le modèle de dépenses:

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

Les méthodes de classe comme celle-ci sont exposées à des associations, et qu'elles sont scope - la différence entre eux et named_scopes est que vous ne pouvez pas les enchaîner.

1

Voici ce qu'il regarderait comme dans Rails 3:

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

AVERTISSEMENT: Attention aux champs chaînés qui ne sont pas enveloppés dans lambda. Vous pouvez penser qu'une partie de la chaîne est évaluée lors de l'exécution, mais elle peut être évaluée au démarrage du serveur d'applications. Assurez-vous que vous enveloppez lambda toute portée qui utilisera ces autres portées.Explication ici: http://www.slashdotdash.net/2010/09/25/rails-3-scopes-with-chaining/

Questions connexes