【问题标题】:Multiple scoped has_many through relationship in Rails 4 [duplicate]Rails 4中的多个作用域has_many通过关系[重复]
【发布时间】:2013-06-27 19:52:31
【问题描述】:

我想对模型 User 和 Event 之间的这种关系进行建模。

因此我开始学习以下课程:

class User < ActiveRecord::Base
...
end

class Attendance < ActiveRecord::Base
# with columns user_id and event_id
...
end

class Event < ActiveRecord::Base
  has_many :attendances
  has_many :users, :through => :attendances
  ...
end

到目前为止一切正常:我可以分配用户并访问考勤。但是现在我想让状态发挥作用,这样我就可以区分例如在“出席”、“无故缺席”、……用户之间。我的第一次尝试是:

class Event < ActiveRecord::Base
  has_many :attendances
  has_many :users, :through => :attendances
  has_many :unexcused_absent_users, -> { where :state => 'unexcused' },
                                   :through => :attendances,
                                   :source => :user
  ...
end

(:source 必须指定,否则它将搜索一个属于名为“unexcused_absent_users”的关联) 这里的问题是,where-predicate 是在表 'users' 上评估的。

我不知道如何“正确”解决这个问题,而无需为每个状态引入新的连接表/模型。尤其是因为每个用户在每个事件中都只能处于一种状态,我认为使用一个出勤模型的解决方案是有意义的。

你有什么想法,如何做到这一点?

【问题讨论】:

    标签: ruby-on-rails activerecord scope has-many-through


    【解决方案1】:

    我找到了解决方法,但我仍然认为,这很难看。

    class Event < ActiveRecord::Base
      has_many :user_attendances, :class_name => 'Attendance'
      has_many :users, :through => :user_attendances, :source => :user
    
      has_many :unexcued_absent_user_attendances, -> { where :state => 'unexcused'}, :class_name => 'Attendance'
      has_many :unexcused_absent_users, :through => :unexcued_absent_user_attendances, :source => :user
    end
    

    一般来说:对于我想要的每个状态,我都必须引入一个新的 has_many 关系,其中包含一个作用域,并在此之上以及一个相应的 has_many-through 关系。

    【讨论】:

    • 我遇到了一个非常相似的问题,并且在 Rails 项目中记录了一个问题 (github.com/rails/rails/issues/10945)。我不是 100% 确定这是一个真正的问题或用户错误,但我最终做了一个与你类似的解决方法。使用这个解决方法我遇到了一些奇怪的东西。例如,我敢打赌这样做:Event.create!(title: 'foo', unexcused_absent_user_ids: [1,2,3]) 会导致state 属性设置为nil。如果您想在 github 上提出我的问题或对您的方法有任何进一步的反馈,我很乐意听到。
    • 我忘了提到我使用了 Event 创建这个例子,因为我认为它与 EventsController#create 可能从用户添加事件的页面收到的 POST 非常相似,同时标记无故缺席的人。 (即像一堆带有学生姓名的复选框)
    【解决方案2】:

    这可能对你有用吗?

    class Event < ActiveRecord::Base
      has_many :attendances
      has_many :users, :through => :attendances
    
      def unexcused_absent_users
        User.joins(:attendances)
          .where(:state => 'unexcused')
          .where(:event_id => self.id)
      end
    end  
    

    在 rails 3+ 中的方法与作用域基本相同,只是不那么令人困惑(在我看来),它们是可链接的

    event = Event.find(xxxx)
    event.unexcused_absent_users.where("name LIKE ?", "Smi%")
    

    【讨论】:

    • 这有一个缺点,即不可写。在我看来,添加另一种方法unexcused_abent_users= array 并不是那么“有条理”:\
    • 上面的方法unexcused_abent_users返回一个ActiveRelation对象而不是一个数组;让“有状态”集合可写对我来说似乎不是一个好主意?您可以使用特定的方法来更改状态,即event.absent_but_not_excused!(user)user.not_excused_from(event) - 猜测这取决于个人喜好
    【解决方案3】:

    您可以简单地缩小范围以查看正确的表格:

      has_many :unexcused_absent_users, -> { where(attendances: {state: 'unexcused'}) },
                                   :through => :attendances,
                                   :source => :user
    

    更好,将此范围添加到出勤模型并将其合并到:

    class Attendance < ActiveRecord::Base
      def self.unexcused
        where state: 'unexcused'
      end
    end
    
    class Event < ActiveRecord::Base
      has_many :unexcused_absent_users, -> { merge(Attendance.unexcused) },
                                   :through => :attendances,
                                   :source => :user      
    end
    

    【讨论】:

    • Attendance 类中使用带有命名方法的合并是干净的。不幸的是,这行不通。 Event 类缺少名为 attendances 的关联,并且由于某种原因,该关联将是只读的。我不知道为什么,但是通过它保存时,范围内设置的state 丢失/忽略/无。
    • @MarioZigliotto:“事件类缺少一个名为出席的关联......” - OP 在初始代码中编写了Event 包含has_many :attendances 行。你能解释一下你的意思吗?在OP给出的解决方案中,他指的是:user_attendances - 也许关联名称已更改?
    猜你喜欢
    • 1970-01-01
    • 2017-04-23
    • 1970-01-01
    • 2014-12-16
    • 1970-01-01
    • 1970-01-01
    • 2011-02-22
    • 1970-01-01
    • 2013-09-29
    相关资源
    最近更新 更多