【问题标题】:How to write a Rails model relation scope as efficiently as possible如何尽可能高效地编写 Rails 模型关系范围
【发布时间】:2014-02-18 21:52:58
【问题描述】:

我有一个用户和一个付款模型。用户有许多付款,而付款有一个用户。为了确定用户是否有有效的付款,我使用这个:

用户.rb

def payment_active?
  payments.where("? >= made_at and ? <= valid_until", DateTime.now, DateTime.now).any?
end

我想定义一个范围,返回活跃付款的用户,我想出了这个:

用户.rb

def self.active_users
  User.all.select {|u| u.payment_active?}
end

编写这样一个作用域的合适、更有效的方法是什么?

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    您可以执行以下操作:

    scope :with_active_payments, ->(datetime = nil) {
      includes(:payments).where('payments.made_at >= :now AND :now <= payments.valid_until', now: datetime || DateTime.current)
    }
    
    • 使用DateTime.current 正确使用您当前的时区。
    • 使用.joins(:payments)(而不是包含)可以使范围返回非唯一的用户列表
    • 您可以将日期时间传递给此范围,而不是现在使用:User.with_active_payments(DateTime.current - 1.months)

    【讨论】:

      【解决方案2】:

      正如 GSP 所说的 ActiveRecord 或对 Mongoid 使用以下查询:

      User.where( :id.in => Payment.where(:made_at <= DateTime.now, :valid_until >= DateTime.now).distinct(:user_id) )
      

      【讨论】:

        【解决方案3】:

        您想对payments 表使用内连接。这可能有效:

        User.joins(:payments).where('payments.made_at <= ? and payments.valid_until >= ?', DateTime.now, DateTime.now) 
        

        作为一个作用域,它可能看起来像这样:

        class User < ActiveRecord::Base
        
          scope :active_users, -> {
            joins(:payments).where('payments.made_at <= ? and payments.valid_until >= ?', DateTime.now, DateTime.now)
          }
        
        end
        

        【讨论】:

        • 这不会返回 uniq 用户列表。使用连接可以复制结果数组中的记录。请改用.includes(:payments)
        • MyYoshiji 有道理。如果您的数据包括多次付款的用户(例如,他们是“双重活跃”),那么每次满足条件时,该用户将被返回一次。
        猜你喜欢
        • 1970-01-01
        • 2017-04-03
        • 2012-12-28
        • 2011-09-19
        • 1970-01-01
        • 2017-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多