has_many :through のNested Associations


RailsTutorialで訳がわからんところがあったのでメモ

Chap 12 - Listing 12.8: Adding the User model following association.

app/models/relationship.rb

class Relationship < ActiveRecord::Base
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
end

ときて

app/models/user.rb

 class User < ActiveRecord::Base
  has_many :microposts, dependent: :destroy
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
  has_many :following, through: :active_relationships, source: :followed
  .
  .
  .
end

↑となるが。class Userの中はなぜ

  has_many :active_relationships, class_name: "Relationship",...
  has_many :following, through: :relationships, ...

↑じゃないのかなと。ていうかhas_manyで事前に定義したAssocって使えるの?なんで?

ActiveRecord::Associations::ClassMethods


activerecord/lib/active_record/associations.rb
をみると。

Nested Associations

You can actually specify any association with the :through option, including an association which has a :through option itself.

Throughに入れるAssociationはなに使ってもいいお。(^ω^ ≡ ^ω^)

ということだそうで。

以下2つは同じことらしい


class Author < ActiveRecord::Base
  has_many :posts
  has_many :comments, through: :posts
  has_many :commenters, through: :comments
end

class Post < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :commenter
end

@author = Author.first
@author.commenters # => People who commented on posts written by the author
class Author < ActiveRecord::Base
  has_many :posts
  has_many :commenters, through: :posts
end

class Post < ActiveRecord::Base
  has_many :comments
  has_many :commenters, through: :comments
end

class Comment < ActiveRecord::Base
  belongs_to :commenter
end

じゃあ書きなおしてみる

ソースコードとか読んでみたけどいまいちよくわからんし。
pryでちまちまプロセスを追ってみたけどヒントにもならず。
ソースの仕組みを書いたブログもあったけどそれでもよくわからない。
とりあえずこのあたりは「こういうふうに動く」と覚えとくかなー

app/models/user.rb
class User < ActiveRecord::Base
  has_many :microposts, dependent: :destroy

  #チュートリアルのやりかた
  has_many :active_relationships, class_name: "Relationship",
    foreign_key: "follower_id", dependent: :destroy
  has_many :followings, through: :active_relationships, source: :followed

  #書き換え1
  #relationships が class Relationshipと同じ名前
  # class_name で「これはエイリアス」と言ってやらなくて良い
  has_many :relationships, foreign_key: "follower_id"
  has_many :followings1, through: :relationships, source: :followed

  #書き換え2
  #ひとつ目はfollower なのに foreign_key: follower_idを指定しないと動かない
  #2つ目はfollowed とfollowed_idと同じ名前なのでsource:でidを指定しなくて良い。
  has_many :follower, class_name: "Relationship", foreign_key: "follower_id"
  has_many :followed, through: :follower
end

基本形が

has_many :(ジョインテーブル名)
has_many :(ジョインの先のテーブル名) through: :(ジョインテーブル名)

だから

要するにこういうことなんだろうなぁと。

  has_many :relationships, (class_name: "Relationship",) foreign_key: "follower_id"
  has_many :followed, through: :relationships, (source: :followed)

関連記事

Nested Associations and Has_many :through, :source
=> has many throughってこう使うんだよ。的な記事。

[Ruby on Rails] ActiveRecord の歩き方
=> 古い記事。掲載されてるソースコードは実際のとかなり違うので注意。
  1から4まである。

Under the hoods of ActiveRecord’s has_many method
=> 三年前の記事

Behind the Scenes of the ‘Has Many’ Active Record Association
=> 日付がないのでいつのかわからないが、Github上の一番新しい奴に近い時点のコードを使ってると思われる。