2010-12-15 76 views
0

J'ai 2 modèles avec plusieurs-à-plusieurs (simplifié ici avec l'exemple des livres-auteurs):Ruby on Rails: trouver ne récupère pas tous les objets associés lors de l'utilisation des conditions

class Book < ActiveRecord::Base 
    has_and_belongs_to_many :authors 
end 

class Author < ActiveRecord::Base 
    has_and_belongs_to_many :books 
end 

Je dois afficher une liste de livres, et sous chacun une liste de ses auteurs. Les utilisateurs peuvent filtrer via un certain auteur.

J'essaie de filtrer via un auteur de la façon suivante:
Supposons qu'il n'y ait qu'un seul livre résultant, et il a 3 auteurs.

books = Book.find(:all, :include => :authors, :conditions => "authors.id = 5") 

Lorsque je tente de lister les différents auteurs des livres récupérés, je reçois seulement l'auteur avec ID = 5.

books.first.authors.size # => 1 
books.first.authors.first.id # => 5 

j'attendre rails pour aller récupérer tous les différents auteurs associés aux livres, que ce soit lors de la requête recherche ou comme une requête de suivi, mais il ne fonctionne pas.

Comment puis-je résoudre ce problème? Y at-il un problème avec les associations? la requête de recherche?

Merci.

Répondre

0

Cela se produit parce que vous utilisez: include dans votre méthode find. Lorsque vous faites cela, vous dites à Rails de mettre en cache le résultat associé (chargement ardent) pour éviter plusieurs requêtes de base de données. Et puisque les conditions que vous avez spécifiées n'incluent que l'un des trois auteurs, c'est le seul qui sera mis en cache puis bouclé lorsque vous appellerez. Auteurs

Je pense que la solution la plus simple serait de changer: include to: joint. Il exécutera la même requête de base de données, mais ne mettra pas en cache les auteurs associés. Ainsi, lorsque vous appelez des auteurs pour chaque livre, il effectue un nouvel appel de base de données pour récupérer tous les auteurs de ce livre. Comme je l'ai dit, le plus simple mais peut-être pas le meilleur, car il pourrait potentiellement entraîner beaucoup de requêtes à la base de données.

Voici une autre façon, vous pouvez le faire:

book_ids = Book.all(:joins => :authors, 
        :conditions => ["authors.id = ?", 5]).map(&:id) 
books = Book.all(:include => :authors, 
       :conditions => ["books.id IN (?)", book_ids]) 

Ce sera d'abord recueillir tous les id des livres liés à un auteur spécifique. Ensuite, il interrogera à nouveau la base de données avec le livre book_ids résultant et cette fois inclure tous les auteurs liés à ces livres et le mettre en cache pour éviter les appels db inutiles.

Modifier:

Si vous voulez vraiment utiliser un seul appel, alors je pense que les seules options est d'écrire un peu plus SQL dans l'appel. Peut-être comme ceci:

books = Book.all(:include => :authors, 
       :conditions => ["books.id IN (SELECT book_id FROM authors_books WHERE author_id = ?)", 5]) 
+0

Merci, j'ai pensé à cette solution, mais j'espérais qu'il existe une solution plus simple. En ce qui concerne les jointures: quand j'utilise des jointures dans cet exemple, j'obtiens un tableau de 3 livres qui sont le même livre, chacun ayant un auteur différent, donc ça ne résout pas le problème ... – Nadav

+0

Okay, quand tu dis ça Par exemple, je suppose que vous voulez dire quand vous changez simplement: inclure à: joint dans votre exemple décrit. Parce que j'ai vérifié le code que j'ai fourni en dernier et ça a marché pour moi. – DanneManne

+0

Oui, votre code fonctionne, mais je ne veux pas le casser dans le code à 2 requêtes différentes. N'existe-t-il pas un moyen d'exécuter une seule requête de rails sans aller chercher manuellement les identifiants, puis extraire les objets eux-mêmes? – Nadav