Rails 活动记录范围基于 has_many 范围的计数
Rails active record scope based on count of has_many scope
我有订单模型和容器模型,范围如下:
class Order < ActiveRecord::Base
has_many :containers, inverse_of: :order, dependent: :destroy
end
class Container < ActiveRecord::Base
scope :full_pickup_ready, -> { where.not(full_pickup_ready_date: nil) }
end
订单模型有一个字段调用 quantity
,它表示订单需要的容器数量,但不一定是容器关联的大小,因为并非所有容器数据都在创建订单时输入。
我想根据 full_pickup_ready_date 的容器数量是否小于订单的数量字段来确定订单模型的范围。
我知道我可以在订单模型上使用合并来访问容器上的范围,如下所示:
def self.at_origin
joins(:containers).merge(Container.full_pickup_ready).uniq
end
但是我如何才能将范围限制为带有 full_pickup_ready_date 的容器总数少于订单上的数量字段的订单?
更新:
这相当接近,但我认为使用 select
效率不高:
includes(:containers).select {|o| o.containers.full_pickup_ready.size < o.quantity }
我认为您需要让这个 SQL 查询为您工作。所以想法是让 SQL 查询正确,然后将其转换为 "Rails".
SQL
如果我没记错的话,这应该是您要实现的 SQL 查询。也许你可以在 rails db
中尝试
SELECT orders.*
FROM orders JOIN containers
WHERE containers.id = orders.id
AND (
SELECT COUNT(containers.id)
FROM containers
WHERE containers.full_pickup_ready_date IS NOT NULL
) < orders.quantity;
ActiveRecord
如果这是正确的查询,那么我们可以使用 rails
Order.joins(:containers).where("( SELECT COUNT(containers.id) FROM containers WHERE containers.full_pickup_ready_date IS NOT NULL ) < orders.quantity")
这应该是 return 一个 ActiveRecord 关系。您也可以这样做:
sql = %{
SELECT orders.*
FROM orders JOIN containers
WHERE containers.id = orders.id
AND (
SELECT COUNT(containers.id)
FROM containers
WHERE containers.full_pickup_ready_date IS NOT NULL
) < orders.quantity;
}.gsub(/\s+/, " ").strip
Order.find_by_sql(sql)
只需将其添加到 class 方法中(比恕我直言的范围更好)就可以了。
因此,您的订单 class 应如下所示:
class Order < ActiveRecord::Base
has_many :containers, inverse_of: :order, dependent: :destroy
def self.gimme_a_query_name
joins(:containers).where("( SELECT COUNT(containers.id) FROM containers WHERE containers.full_pickup_ready_date IS NOT NULL ) < orders.quantity")
end
def self.gimme_another_query_name
sql = %{
SELECT orders.*
FROM orders JOIN containers
WHERE containers.id = orders.id
AND (
SELECT COUNT(containers.id)
FROM containers
WHERE containers.full_pickup_ready_date IS NOT NULL
) < orders.quantity;
}.gsub(/\s+/, " ").strip
find_by_sql(sql)
end
end
我无法尝试这个,但它应该可以通过一些调整来获得正确的 SQL 查询。
希望对您有所帮助!
如果您愿意放弃在 Container
上重复使用范围,那么您应该可以使用类似的东西:
# scope on Order
joins(:containers)
.group("orders.id")
.having("count(CASE WHEN full_pickup_ready_date THEN 1 END) < orders.quantity")
我有订单模型和容器模型,范围如下:
class Order < ActiveRecord::Base
has_many :containers, inverse_of: :order, dependent: :destroy
end
class Container < ActiveRecord::Base
scope :full_pickup_ready, -> { where.not(full_pickup_ready_date: nil) }
end
订单模型有一个字段调用 quantity
,它表示订单需要的容器数量,但不一定是容器关联的大小,因为并非所有容器数据都在创建订单时输入。
我想根据 full_pickup_ready_date 的容器数量是否小于订单的数量字段来确定订单模型的范围。
我知道我可以在订单模型上使用合并来访问容器上的范围,如下所示:
def self.at_origin
joins(:containers).merge(Container.full_pickup_ready).uniq
end
但是我如何才能将范围限制为带有 full_pickup_ready_date 的容器总数少于订单上的数量字段的订单?
更新:
这相当接近,但我认为使用 select
效率不高:
includes(:containers).select {|o| o.containers.full_pickup_ready.size < o.quantity }
我认为您需要让这个 SQL 查询为您工作。所以想法是让 SQL 查询正确,然后将其转换为 "Rails".
SQL
如果我没记错的话,这应该是您要实现的 SQL 查询。也许你可以在 rails db
SELECT orders.*
FROM orders JOIN containers
WHERE containers.id = orders.id
AND (
SELECT COUNT(containers.id)
FROM containers
WHERE containers.full_pickup_ready_date IS NOT NULL
) < orders.quantity;
ActiveRecord
如果这是正确的查询,那么我们可以使用 rails
Order.joins(:containers).where("( SELECT COUNT(containers.id) FROM containers WHERE containers.full_pickup_ready_date IS NOT NULL ) < orders.quantity")
这应该是 return 一个 ActiveRecord 关系。您也可以这样做:
sql = %{
SELECT orders.*
FROM orders JOIN containers
WHERE containers.id = orders.id
AND (
SELECT COUNT(containers.id)
FROM containers
WHERE containers.full_pickup_ready_date IS NOT NULL
) < orders.quantity;
}.gsub(/\s+/, " ").strip
Order.find_by_sql(sql)
只需将其添加到 class 方法中(比恕我直言的范围更好)就可以了。
因此,您的订单 class 应如下所示:
class Order < ActiveRecord::Base
has_many :containers, inverse_of: :order, dependent: :destroy
def self.gimme_a_query_name
joins(:containers).where("( SELECT COUNT(containers.id) FROM containers WHERE containers.full_pickup_ready_date IS NOT NULL ) < orders.quantity")
end
def self.gimme_another_query_name
sql = %{
SELECT orders.*
FROM orders JOIN containers
WHERE containers.id = orders.id
AND (
SELECT COUNT(containers.id)
FROM containers
WHERE containers.full_pickup_ready_date IS NOT NULL
) < orders.quantity;
}.gsub(/\s+/, " ").strip
find_by_sql(sql)
end
end
我无法尝试这个,但它应该可以通过一些调整来获得正确的 SQL 查询。
希望对您有所帮助!
如果您愿意放弃在 Container
上重复使用范围,那么您应该可以使用类似的东西:
# scope on Order
joins(:containers)
.group("orders.id")
.having("count(CASE WHEN full_pickup_ready_date THEN 1 END) < orders.quantity")