为关联模型创建Rails范围

时间:2013-05-27 11:08:22

标签: ruby-on-rails activerecord

我有2个型号:

DeliverySlot has_many:orders 订单belongs_to:delivery_slot

交货时段限制了他们可以持有的订单数量。我想创建一个范围来提供所有可用的交付槽。可用的交付槽是未达到相关订单限制的槽。

我的尝试看起来像:

scope :available, where("limit > ?", order.count).joins(:orders)

order.count是上面的伪代码。

3 个答案:

答案 0 :(得分:2)

要像设置设置一样执行此操作,您需要使用orders.count而不是order.count,因为您指的是关联。这会提示ActiveRecord汇编类似SELECT COUNT(*) FROM orders WHERE delivery_slot_id = 1的查询。

Rails实际上非常聪明,当你恰当地传递它时,where条件中的子查询可以作为子查询使用,where('limit > ', orders.count)。但是正如您可能看到的,如果它是预编译的,那么这将无效,因为查询在条件中使用了显式ID。

您需要的是计算具有模糊条件的订单,然后将其用作子查询:where('limit > ?', Order.where(delivery_slot_id: 'delivery_slots.id').count)。如果您尝试单独运行查询订单计数,它将在delivery_slots上失败,但因为它位于子查询中,所以您应该顺利航行。

我想提出另一种完全相同的方法,使用计数器缓存:

class AddCounterCacheToDeliverySlots < ActiveRecord::Migration
  class DeliverySlot < ActiveRecord::Base; end
  def change
    add_column :delivery_slots, :orders_count, :integer, default: 0
    add_index :delivery_slots, [:orders_count, :limit]

    DeliverySlot.reset_column_information
    DeliverySlot.find_each do |delivery_slot|
      DeliverySlot.update_counters delivery_slot.id, orders_count: delivery_slot.orders.count
    end
  end
end

class Order < ActiveRecord::Base
  belongs_to :delivery_slot, counter_cache: true
end

class DeliverySlot < ActiveRecord::Base
  has_many orders
  scope :available, where('orders_count < limit')
end

Rails将自动递增和递减每个orders_count的{​​{1}}列,并且由于它已编入索引,因此查询速度非常快。

答案 1 :(得分:0)

scope :available, lambda { 
  |delivery_slot| joins(:orders).
         where("limit > ?", order.count) 
}

试试这个

答案 2 :(得分:0)

所以我找到了一种在SQL中实现它的方法。如果有人知道更多ruby方式而不创建大量数据库查询,请跳进去。

scope :available, joins('LEFT JOIN orders 
    ON orders.delivery_slot_id = delivery_slots.id')
    .where("delivery_slots.limit > ( 
        SELECT COUNT(*) FROM orders 
        WHERE orders.delivery_slot_id = delivery_slots.id )
    ")