关于activerecord:为关联模型创建Rails范围

Create Rails scope for associated model

我有2个型号:

DeliverySlot has_many:orders
订单归属于:delivery_slot

送货位置限制了其可容纳的订单数量。我想创建一个合并范围以提供所有可用的交付位置。可用的交货时间是尚未达到相关订单限制的时间。

我的尝试如下:

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

order.count是上面的伪代码。


要像完成设置一样执行此操作,则需要使用orders.count而不是order.count,因为您是指关联。这将提示ActiveRecord组装看起来像SELECT COUNT(*) FROM orders WHERE delivery_slot_id = 1的查询。

Rails实际上足够聪明,可以在适当地传递它时将其用作where条件中的子查询,即la where('limit > ', orders.count)。但是,您可能会看到,如果它是预编译的,这将不起作用,因为查询在条件中使用了显式ID。

您需要的是对条件不明确的订单进行计数,然后将其用作子查询:where('limit > ?', Order.where(delivery_slot_id: 'delivery_slots.id').count)。如果尝试单独运行查询以获取订单数,则该查询将在delivery_slots上失败,但是由于它位于此处的子查询中,因此应该顺畅运行。

尽管如此,我还是想提出另一种使用计数器缓存的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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会自动为每个DeliverySlot递增和递减orders_count列,并且由于已对其进行索引,因此查询起来非常快。


因此,我找到了一种在SQL中执行此操作的方法。如果有人知道一种更ruby的方式而不创建数据库查询负载,请跳入。

1
2
3
4
5
6
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 )
   ")

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

试试这个