

ruby 2.2.1p85  
rails 4.2.1

以下のようなmodel構成で実装しております。

[code]

    class Student
      after_initialize :set_profile
      has_one :student_profile, dependent: :destroy
    
      def set_profile
        self.student_profile ||= StudentProfile.new
      end
    end
    
    class StudentProfile
      belong_to :student
    end
    
[/code]

その上で、n+1問題を解決したく以下のようにすると、先にafter_initializeが発生してしまい、n+1問題が解決しませんでした。

Student.joins(:student_profile)  
もしくは  
Student.includes(:student_profile)

after_initializeの処理はいじりたくなく、それ以外の方法でn+1問題を解決する方法はありますでしょうか？

宜しくお願いします。


CC BY-SA 3.0
2016-02-18T07:23:21.770
22228
2016-04-18T09:38:44.677
13175
question
ruby-on-rails ruby
after_initializeが先に実行されることにより、joinsやincludesを使ったn+1問題が解消されません。


やりたいことは、`Student#student_profile` を呼び出した際に必ず `StudentProfile` のインスタンスが返るようにしたい
(`nil` にしたくない) ということだと思います。これを実現するため、`student_profile`
メソッドを置き換えて、呼ばれた際に初めて初期化する方法があります。

[code]

    class Student < ActiveRecord::Base
      has_one :student_profile, dependent: :destroy
    
      def student_profile_with_init
        self.build_student_profile if not self.student_profile_without_init
        self.student_profile_without_init
      end
    
      alias_method_chain :student_profile, :init
    end
    
[/code]

以上の設定をした上で、`Student.eager_load(:student_profile)` または
`Student.includes(:student_profile)` と呼び出すことで、N+1 を回避できます。

**注記**

  * `alias_method_chain` によって、`student_profile_with_init` が `student_profile` になり、元のメソッドは `student_profile_without_init` として保存されます。
  * `build_student_profile` はアソシエーションによって、自動的に生成されるメソッドで、やっていることは `self.build_student_profile = StudentProfile.new` と同じです。


CC BY-SA 3.0
2016-02-18T08:13:01.160
22229
2016-02-18T08:13:01.160
76
22228
answer