如何通过Rails ActiveRecord中的连接正确别名has_many?

时间:2015-01-31 01:42:47

标签: ruby-on-rails ruby activerecord

Rails / ActiveRecord新手在这里。考虑以下用于课堂,用户和课堂注册的模型(在两者之间加入)

class Classroom < ActiveRecord::Base
  has_many :fulltime_enrollments, -> { where(duration: 'full-time') }, class_name: "ClassroomEnrollments"
  has_many :fulltimers, :through => :fulltime_enrollments, class_name: "User"

  has_many :parttime_enrollments, -> { where(duration: 'part-time') }, class_name: "ClassroomEnrollments"
  has_many :parttimers, :through => :parttime_enrollments, class_name: "User"
end

class ClassroomEnrollment < ActiveRecord::Base
  # columns: user_id, classroom_id, duration
  belongs_to :user
  belongs_to :classroom
end

class User < ActiveRecord::Base
  has_many :classroom_enrollments
  has_many :classrooms, :through => :classroom_enrollments
end

教室和课堂教程的以下模型不起作用。当我尝试通过:fulltimers:parttimers访问时,undefined method 'to_sym' for nil:NilClassmy_classroom.fulltimers别名会导致my_classroom.parttimers错误。

如果我删除了:parttimers别名并将:fulltimers重命名为:users,它可以正常工作(并且只显示全职学生),所以在我看来它有事情要做即使我在:fulltimers条件中指定了User,也可以确定classname: "User"属于has_many类型。

我做错了什么?

2 个答案:

答案 0 :(得分:5)

由于无法自动推断源关联,因此您需要使用:source选项指定它:

class Classroom < ActiveRecord::Base
  has_many(
    :fulltime_enrollments, 
    -> { where(duration: 'full-time') }, 
    class_name: "ClassroomEnrollments"
  )
  has_many :fulltimers, :through => :fulltime_enrollments, :source => :user

  has_many(
    :parttime_enrollments, 
    -> { where(duration: 'part-time') }, 
    class_name: "ClassroomEnrollments"
  )
  has_many :parttimers, :through => :parttime_enrollments, :source => :user
end

http://guides.rubyonrails.org/association_basics.html#options-for-has-many-source

尝试更清晰,更易读的方法怎么样?像这样:

class Classroom < ActiveRecord::Base
  has_many :classroom_enrollments
  has_many :users, through: :classroom_enrollments

  def full_timers
    users_by_duration("full-time")
  end

  def part_timers
    users_by_duration("part-time")
  end

  private

  def users_by_duration(duration)
    users.where(classroom_enrollments: { duration: duration })
  end
end

然后:

my_classroom = Classroom.find(1)
my_classroom.full_timers

答案 1 :(得分:1)

我在做类似的事情时偶然发现了这一点。这将生成相同的sql并且更容易查看。

class Classroom < ActiveRecord::Base
  has_many :classroom_enrollments
  has_many :users, through: :classroom_enrollments

  def fulltimers
    users.merge(ClassroomEnrollment.full_time)
  end

  def parttimers
    users.merge(ClassroomEnrollment.part_time)
  end
end

class ClassroomEnrollment < ActiveRecord::Base
  belongs_to :user
  belongs_to :classroom

  scope :fulltime, ->{ where(duration: 'full-time') }
  scope :parttime, ->{ where(duration: 'part-time') }
end

class User < ActiveRecord::Base
  has_many :classroom_enrollments
  has_many :classrooms, :through => :classroom_enrollments
end