2017-04-25 2 views
0

J'ai trois modèles à l'aide des associations Active Record:Ajouter attribut imbriqué au modèle associé à Rails

Book Model 
has_many :checkouts 

User Model 
has_many :checkouts 

Checkout Model 
belongs_to :book 
belongs_to :user 

À mon avis, ai-je besoin du livre, caisse et noms d'utilisateurs des caisses.

En utilisant Book.first.checkouts je reçois:

#<ActiveRecord::AssociationRelation 
    [#<Checkout id: 30, 
    checkout_date: "2017-04-13", 
    return_date: nil, 
    book_id: 118, 
    user_id: 1, 
    created_at: "2017-04-13 17:43:07", 
    updated_at: "2017-04-13 17:43:07" 
    >,#<Checkout id: 50, 
    checkout_date: "2017-04-13", 
    return_date: nil, 
    book_id: 118, 
    user_id: 1, 
    created_at: "2017-04-14 00:33:34", 
    updated_at: "2017-04-14 00:33:34"> 
    ]> 

Mais, je voudrais le nom d'utilisateur, et pas seulement l'id. J'ai essayé Book.first.checkouts.map { |c| c.user.name } mais cela renvoie seulement le nom, et j'ai besoin du reste de l'information de caisse. Idéalement, mes données (converties en JSON) ressemble à quelque chose comme:

{ 
    name: "Book Name", 
    checkouts: [ 
    checkout_data: "Today", 
    user_name: "Mary" 
    ] 
} 

Comment puis-je ajouter le nom d'utilisateur à mes données de caisse?

+0

Affichez-vous les extractions individuellement, c'est-à-dire une boucle '.each'? Vous devriez être capable d'afficher 'checkout.user.name' – Okomikeruko

+0

@Okomikeruko oui, individuellement à travers une boucle. 'checkout.user.name' fonctionne, mais il ne renvoie que le nom d'utilisateur. J'ai besoin de la caisse et du nom d'utilisateur. Je me demande si je peux l'ajouter à un attribut virtuel, ou quelque chose comme ça. – BHOLT

+0

Je suppose que j'essaie de combiner les données de 2 objets: 'checkouts' et' user' – BHOLT

Répondre

1

Vous pouvez essayer à votre contrôleur:

render json: @books, include: { checkout: { only: :checkout_date, include: { user: { only: :name } } }}

+0

Ne savait pas ce! C'est vraiment sympa – BHOLT

0

Vous devriez précharger les données pour éviter (N + 1) problème de requête, pour l'affichage possibles checkouts pour livre particulier:

book_id = <given book id> 
book = Book.find(book_id) 
checkouts = book.checkouts.includes(:user) 
return_hash = {name: book.name, checkouts: []} 
checkouts.each do |checkout| 
    return_hash[:checkouts] << { checkout_data: checkout.checkout_date, 
           user_name: checkout.user.name 
           } 
end 
+0

C'est un peu ce que j'essayais, mais je me demandais s'il y avait un moyen plus simple. Quel est le problème de requête N + 1? – BHOLT

+1

checkout.user lancera 1 requête à la base de données pour chaque extraction, si vous utilisez includes: user alors tous les utilisateurs correspondants seront pré-recherchés dans la requête IN – aqfaridi

0

Pour inclure d'autres solutions, j'ai trouvé que cela fonctionnait plutôt bien:

checkouts = book.checkouts.unreturned.map do |checkout| 
    checkout.attributes.merge({ user_name: checkout.user.name }) 
end 

{ checkouts: checkouts, available: book.available?, book_id: book.id } 

attributes.merge a fait l'affaire.

+0

Ne pas utiliser. attributes.merge dans une boucle, il faudra beaucoup de temps d'exécution que votre application se développe, j'ai fait face au même problème de performance dans mon application, utilisez ruby-prof pour voir le goulot d'étranglement dans votre code – aqfaridi