Rails中如何正确加载用户的已支付CourseRun关联数据?
解决用户已支付CourseRun的ActiveRelation查询及作用域实现
直接获取ActiveRelation的写法
要返回可链式调用的ActiveRecord::Relation,不要先查询payments再取关联,而是直接在course_runs关联上通过joins和where过滤支付状态:
user.course_runs.joins(:payments).where(payments: { aasm_state: 'paid' })
这种方式不会立即执行查询,返回的结果可以继续链式添加过滤条件(比如按时间筛选)。
用作用域优化实现
1. 先给Payment模型添加状态作用域
在Payment类中定义复用的状态作用域,让状态判断更清晰:
class Payment < ApplicationRecord belongs_to :user belongs_to :course_run aasm do state :new, initial: true state :paid state :canceled state :refunded # ... 其他状态和事件 end scope :paid, -> { where(aasm_state: 'paid') } end
2. 在User模型中定义实例方法/作用域
在User类中封装已支付课程的查询逻辑,方便调用:
class User < ApplicationRecord has_many :payments has_many :course_runs, through: :payments # 实例方法:获取当前用户的已支付课程(返回ActiveRelation) def paid_course_runs course_runs.joins(:payments).merge(Payment.paid) end # 类级作用域:筛选出拥有已支付课程的用户(可选) scope :with_paid_course_runs, -> { joins(course_runs: :payments).merge(Payment.paid) } end
调用示例:
# 获取当前用户已支付的课程,可继续链式过滤 user.paid_course_runs.where(start_date: Date.today..) # 筛选出所有有已支付课程的用户 User.with_paid_course_runs
3. (可选)在CourseRun模型中添加作用域
如果需要从课程维度查询某用户的已支付记录,可在CourseRun类中定义:
class CourseRun < ApplicationRecord has_many :payments has_many :users, through: :payments scope :paid_by_user, ->(user) { joins(:payments).where(payments: { user_id: user.id, aasm_state: 'paid' }) } end
调用示例:
CourseRun.paid_by_user(user)
为什么原写法返回数组
你之前的user.payments.where(aasm_state == 'paid').course_runs会先执行payments的查询,返回Payment的集合(此时已触发数据库查询),再调用course_runs会将关联的课程加载为数组,因此无法继续链式调用。而通过在course_runs关联上直接拼接条件,能保持ActiveRelation的延迟查询特性。
内容的提问来源于stack exchange,提问作者Klemikaze
相关产品推荐
相关产品推荐

