Skip to content

Les associations en Rails: Partie 03

Bouzid Badreddine edited this page Oct 15, 2020 · 2 revisions

has_many

Qu'est ce que c'est?

Dans la leçon précédente on a vu comment accéder au livre associé à l'auteur à partir de l'auteur directement, en utilisant has_one.

Si l'auteur peut avoir plusieurs livres, il serait convenable de pouvoir y accéder (aux livres) à partir des objets Author, pour les lires et les modifier. Cela est possible en utilisant has_many.

Comment l'utiliser?

Il suffit d'appeler la méthode has_many dans la classe Author avec comme argument un symbole représentant le modèle Book en pluriel (remarquez la différence avec has_one :book, singulier).

class Author < ApplicationRecord
  has_many :books
end

Maintenant on peut attribuer plusieurs livres à un auteur directement à partir des instances de Author:

2.4.0 :003 > ahmed = Author.new name: "Ahmed"
 => #<Author id: nil, name: "Ahmed", created_at: nil, updated_at: nil> 
2.4.0 :004 > book1 = Book.new title: "Quantum Mechanics"
 => #<Book id: nil, title: "Quantum Mechanics", author_id: nil, created_at: nil, updated_at: nil> 
2.4.0 :005 > book2 = Book.new title: "Algebra"
 => #<Book id: nil, title: "Algebra", author_id: nil, created_at: nil, updated_at: nil> 
2.4.0 :006 > book1.valid?
 => false 
2.4.0 :007 > book1.errors.messages
 => {:author=>["must exist"]} 
2.4.0 :009 > ahmed.valid?
 => true 
2.4.0 :010 > ahmed.books
 => #<ActiveRecord::Associations::CollectionProxy []> 
2.4.0 :011 > ahmed.books.size
 => 0 
2.4.0 :012 > ahmed.books << book1
 => #<ActiveRecord::Associations::CollectionProxy [#<Book id: nil, title: "Quantum Mechanics", author_id: nil, created_at: nil, updated_at: nil>]> 
2.4.0 :013 > ahmed.books.size    
 => 1 
2.4.0 :014 > ahmed.books.count                                                                             
 => 0 
2.4.0 :015 > ahmed.books << book2
 => #<ActiveRecord::Associations::CollectionProxy [#<Book id: nil, title: "Quantum Mechanics", author_id: nil, created_at: nil, updated_at: nil>, #<Book id: nil, title: "Algebra", author_id: nil, created_at: nil, updated_at: nil>]> 
2.4.0 :016 > ahmed.books.size    
 => 2 
2.4.0 :017 > ahmed.books.count   
 => 0 
2.4.0 :018 > ahmed.save
   (0.2ms)  begin transaction
  SQL (1.4ms)  INSERT INTO "authors" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", "Ahmed"], ["created_at", "2017-07-13 11:23:47.766572"], ["updated_at", "2017-07-13 11:23:47.766572"]]
  SQL (0.2ms)  INSERT INTO "books" ("title", "author_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["title", "Quantum Mechanics"], ["author_id", 1], ["created_at", "2017-07-13 11:23:47.771789"], ["updated_at", "2017-07-13 11:23:47.771789"]]
  SQL (0.1ms)  INSERT INTO "books" ("title", "author_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["title", "Algebra"], ["author_id", 1], ["created_at", "2017-07-13 11:23:47.772789"], ["updated_at", "2017-07-13 11:23:47.772789"]]
   (119.4ms)  commit transaction
 => true 
2.4.0 :019 > ahmed.books.size 
 => 2 
2.4.0 :020 > ahmed.books.count
   (0.5ms)  SELECT COUNT(*) FROM "books" WHERE "books"."author_id" = ?  [["author_id", 1]]
 => 2 
2.4.0 :021 > book1.reload.author.name
  Book Load (0.7ms)  SELECT  "books".* FROM "books" WHERE "books"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Author Load (0.1ms)  SELECT  "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => "Ahmed" 
2.4.0 :022 > ahmed.reload.books.first.title
  Author Load (0.3ms)  SELECT  "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Book Load (0.5ms)  SELECT  "books".* FROM "books" WHERE "books"."author_id" = ? ORDER BY "books"."id" ASC LIMIT ?  [["author_id", 1], ["LIMIT", 1]]
 => "Quantum Mechanics" 
2.4.0 :023 > ahmed.reload.books.second.title
  Author Load (0.4ms)  SELECT  "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Book Load (0.5ms)  SELECT  "books".* FROM "books" WHERE "books"."author_id" = ? ORDER BY "books"."id" ASC LIMIT ? OFFSET ?  [["author_id", 1], ["LIMIT", 1], ["OFFSET", 1]]
 => "Algebra"

Plusieurs remarques s'imposent:

  • Il y a un différence entre la méthode size et la méthode count, size retourne la taille de la liste .books alors que count retourne le nombre de ces livres en faisant une requête dans la base de données (ou en puisant dans la cache quand c'est possible). Avant la sauvegarde size donnait un nombre non nul alors que count donnait 0. Après la sauvegarde tout deux donnaient la même valeur.
  • .books se comporte maintenant plus comme un array, c'est pour cela qu'on a pu appeler .size et << sur lui.
  • La sauvegarde d'un objet enclenche la sauvegarde des objets qui lui sont associés.

ERD