Question sur directive :include dans la méthode 'find'

Bonjour à tous,
en faisant par exemple

c = Client.find(:first, :include => :commandes)

je m’attends à ce que les commandes du client soient toutes préchargées
avec mon client c

lorsque je veux rechercher des commandes parmis toutes celles
préchargées par la méthode précédente :

c.commandes.find(:first, :conditions => “expédiée = ‘O’”)

je vois dans le fichier development.log qu’il me refait une requête sql
(de type select … where expédiée=‘O’ limit 1) avec des messages
“Commande load, etc…” comme si les commandes n’étaient pas préchargées

est-ce le comportement normal de rails ou est-ce que j’oublie qqch /
m’en sert mal ?

Nicolas wrote:

Bonjour à tous,

Bonjour,

en faisant par exemple

c = Client.find(:first, :include => :commandes)

je m’attends à ce que les commandes du client soient toutes préchargées
avec mon client c

Tout dépend de ce que tu entends par préchargées.

Quand tu fais cela, activeRecord récupère tout dans la base en une
requête à l’aide d’un joint. Donc les commandes tel que définies dans
ton has_many sont effectivements disponibles sans requête supplémentaire
mais en tant qu’objet AR.

est-ce le comportement normal de rails ou est-ce que j’oublie qqch /
m’en sert mal ?

Tu t’en sert mal en fait. La fonction ActiveRecord#find n’est rien
d’autre qu’un binding mysql donc tu ne peux pas rechercher dans des
objets AR avec. Ca sert à récupérer des données d’une base de donnée
sous la forme d’objet AR.

Donc les commandes sont préchargées mais toi tu relances une recherche
mysql ce qui explique qu’elle apparaisse dans le log.

Pour faire ce que tu veux, tu as plusieurs bonnes méthodes:

-une lente en ruby:
c.commandes.find_all { |c| c.expédiée == ‘O’}

solution adaptée si tu n’as pas sur ta page une longue liste de client
ou bcp de commandes par client. Dans le cas contraire c’est un perf
killer.

-une rapide générique en utilisant rails qui consiste à faire un
has_many particulier avec ton critère, à toi d’utiliser l’include
souhaité:
has_many :delivered_order,
:conditions => “expédiée=‘O’”,
:class_name => “Commande”

-une rapide ponctuelle en utilisant rails
Commande.with_scope(:find => { :conditions => “expédiée = ‘O’” }) do
c = Client.find(:first, :include => :commandes)
end

Voilou,
Renaud

Nicolas wrote:

Merci Renaud,
j’ai parfaitement compris les deux premières solutions et je vais mettre
en oeuvre la deuxième (en gardant la première dans un coin de ma mémoire
ça peut dépanner ponctuellement…)
en revanche je ne suis pas assez calé en rails pour comprendre la
troisième méthode qui utilise “with_scope”. j’ai regardé la doc, très
succinte, mais ça ne m’a pas plus avancé hélas.

Rails ne propose pas d’outils très développés pour construire des
requêtes dynamiquement (Pas de critère orienté objet par exemple) sauf Ã
trifouiller à la main dans du mysql ou utiliser des methodes protégées
comme merge_conditions.

Les scopes permettent sont une facon élégante de modifier des critères,
c’est loin d’être encore parfait mais c’est déjà bien utile. Ca marche
pour la création ou la recherche/update.

Plus d’info ici: http://habtm.com/articles/2006/02/22/nested-with_scope

L’exemple que je t’ai donné est faux, car le include génère la jointure
et le scope ne fonctionne pas dessus donc mauvais exemple.

Ca marche si tu retires l’include mais ca génère une requête sql Ã
chaque fois donc ca ne t’intéresse pas. Donc dans ton cas précis ca ne
résoud pas le problème.

Renaud

Renaud (Nel) Morvan wrote:

Nicolas wrote:

Bonjour à tous,

Bonjour,

en faisant par exemple

c = Client.find(:first, :include => :commandes)

je m’attends à ce que les commandes du client soient toutes préchargées
avec mon client c

Tout dépend de ce que tu entends par préchargées.

Quand tu fais cela, activeRecord récupère tout dans la base en une
requête à l’aide d’un joint. Donc les commandes tel que définies dans
ton has_many sont effectivements disponibles sans requête supplémentaire
mais en tant qu’objet AR.

est-ce le comportement normal de rails ou est-ce que j’oublie qqch /
m’en sert mal ?

Tu t’en sert mal en fait. La fonction ActiveRecord#find n’est rien
d’autre qu’un binding mysql donc tu ne peux pas rechercher dans des
objets AR avec. Ca sert à récupérer des données d’une base de donnée
sous la forme d’objet AR.

Donc les commandes sont préchargées mais toi tu relances une recherche
mysql ce qui explique qu’elle apparaisse dans le log.

Pour faire ce que tu veux, tu as plusieurs bonnes méthodes:

-une lente en ruby:
c.commandes.find_all { |c| c.expédiée == ‘O’}

solution adaptée si tu n’as pas sur ta page une longue liste de client
ou bcp de commandes par client. Dans le cas contraire c’est un perf
killer.

-une rapide générique en utilisant rails qui consiste à faire un
has_many particulier avec ton critère, à toi d’utiliser l’include
souhaité:
has_many :delivered_order,
:conditions => “expédiée=‘O’”,
:class_name => “Commande”

-une rapide ponctuelle en utilisant rails
Commande.with_scope(:find => { :conditions => “expédiée = ‘O’” }) do
c = Client.find(:first, :include => :commandes)
end

Voilou,
Renaud

Merci Renaud,
j’ai parfaitement compris les deux premières solutions et je vais mettre
en oeuvre la deuxième (en gardant la première dans un coin de ma mémoire
ça peut dépanner ponctuellement…)
en revanche je ne suis pas assez calé en rails pour comprendre la
troisième méthode qui utilise “with_scope”. j’ai regardé la doc, très
succinte, mais ça ne m’a pas plus avancé hélas.
encore merci pour ton aide précieuse
bonne journée,
Nicolas

regarder le plugin ez-where

Client.find_where(:first, :include => [:commandes]) do |client,
commande|
commande.expediee == ‘O’
end