はじめに
この記事は2023年度の振り返りです。
こちらの続きとなります。
問題を解いてる時の実話を元に、作成しております
勉強会内の小話
講師:前回、joinの中で紐づけをしてたけど、直接呼べるようにリレーションを組んでおけば楽なので、今回はそれをやってみよう!
初学者達:わかりました!has_manyのthroughですよね!参考書で勉強してる時に読みました!
講師:知ってるなら話は早い。やってみよう!
初学者達:はい!
初学者達:こんな感じでどうでしょう!
class Comic < ApplicationRecord
has_many :writes, foreign_key: 'book_id'
has_many :author, through: :writes
end
p Comic.joins(:author).where(author: { name: '冨樫 義博' })
講師:発行されるクエリを確認してみよう
SELECT `comics`.*
FROM `comics`
INNER JOIN `writes` ON `writes`.`book_id` = `comics`.`id`
INNER JOIN `authors` `author` ON `author`.`id` = `writes`.`user_id`
WHERE `author`.`name` = '冨樫 義博'
講師:うん。前回と同じになって、rubyのソースは少し短くできたね。ここの問題としては終了だけど、せっかくhas_manyを触ってるから深堀をしてみよう。
講師:今リレーションの名前をモデルもしくはテーブル名を割り当ててるけど、それを変えてみよう
初学者達:ん?こんな感じですか?
class Comic < ApplicationRecord
has_many :kaku, foreign_key: 'book_id'
end
初学者達:これ絶対つながらないので、どのクラスにつなぐかオプションが必要ですよね。。。
初学者達:これでどうでしょう?
class Comic < ApplicationRecord
has_many :kaku, foreign_key: 'book_id', class_name: 'Write'
end
講師:確認してみよう
comic = Comic.all.first
comic.kaku
SELECT `writes`.* FROM `writes` WHERE `writes`.`book_id` = 1
講師:OK。あってるね。これも前回同様にリレーション名に関連モデル名を指定したことで紐づけするクラス名をrailsが解釈をしててくれてたわけだ。
初学者達:なるほど・・・
講師:じゃぁ、同じ要領で他のも修正してみよう
初学者達:はい!
初学者達:せんせー。ここまで書いたんですが、詰まりました!
class Comic < ApplicationRecord
has_many :kaku, foreign_key: 'book_id', class_name: 'Write'
has_many :kaita_hito, through: :kaku
end
class Write < ApplicationRecord
belongs_to :hito, foreign_key: 'user_id', class_name: 'Author'
belongs_to :manga, foreign_key: 'book_id', class_name: 'Comic'
end
class Author < ApplicationRecord
has_many :kaita, foreign_key: 'user_id', class_name: 'Write'
end
comic = Comic.all.first
comic.kaita_hito
/usr/local/bundle/gems/activerecord-7.1.2/lib/active_record/reflection.rb:1092:in `check_validity!': Could not find the source association(s) "kaitahito" or :kaitahito in model Write. Try 'has_many :kaitahito, :through => :kaku, :source => <name>'. Is it one of hito or manga? (ActiveRecord::HasManyThroughSourceAssociationNotFoundError)
講師:エラーに全てヒントが出てるじゃん。。。
初学者達:sourceを使えっていうのは分かったんですが、なんで「hito or manga」なのかがイマイチ。。。
初学者達:なるほど!。sourceって、リレーション先でのリレーション名を指定するのですね!
初学者達:ん?ってことは、前回のも?
講師:そう。前回のcomicモデルにauthorのリレーションを組んだ際も、railsがsourceはauthorと解釈して、こんな感じにリレーションを組んでくれてたんだ。
初学者達:へーーー。すごいですね
講師:そうだな。ただ、今回はリレーション名を関連モデル名とは異なる名称にしてるので、自分で指定してやる必要がある
初学者達:納得しました!これでどうでしょう!?
class Comic < ApplicationRecord
has_many :kaku, foreign_key: 'book_id', class_name: 'Write'
has_many :kaita_hito, through: :kaku, source: :hito
end
class Write < ApplicationRecord
belongs_to :hito, foreign_key: 'user_id', class_name: 'Author'
belongs_to :manga, foreign_key: 'book_id', class_name: 'Comic'
end
class Author < ApplicationRecord
has_many :kaita, foreign_key: 'user_id', class_name: 'Write'
end
講師:確認してみよう
comic = Comic.all.first
comic.kaita_hito
SELECT `authors`.*
FROM `authors`
INNER JOIN `writes` ON `authors`.`id` = `writes`.`user_id`
WHERE `writes`.`book_id` = 1
=>
[#<Author:0x00007f18600fad50
id: 1,
name: "岸本 斉史",
created_at: Sat, 23 Dec 2023 02:56:25.453889000 UTC +00:00,
updated_at: Sat, 23 Dec 2023 02:56:25.453889000 UTC +00:00>]
講師:うんいい感じですね。
初学者達:やったー!
講師:次回は、さらに発展型をしてみよう
次回へ続く(次回が最終回です)
さいごに
ここはよく甘い認識のまま利用してました!っていう声が多かったので、常々記事にしたいと思っていました。
この部分を説明したいがゆえに、長い説明を入れていたといっても過言ではありません。(前部分が理解できていないと関係上、説明しづらいのです。)
ここまで連続で読まれた方、読みにくい文章をご拝読頂きましてありがとうございました。次回が最終回となります。最後まで読んで頂けますと嬉しい限りです。