我的Rails(4.2)电子商务应用程序中有以下结构:
class Product < ActiveRecord::Base
has_many :prices
def best_price(price_groups)
prices.where(price_group_id: price_groups).minimum(:value)
end
def default_price
prices.where(price_group_id: 1).first.value
end
end
class Price < ActiveRecord::Base
belongs_to :price_group
belongs_to :product
end
class PriceGroup < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :prices
end
class User < ActiveRecord::Base
has_and_belongs_to_many :price_groups
end
许多用户可以是许多价格组的成员,每个价格组中都有许多产品价格行。 有一个默认组,每个产品的默认价格加上可以有许多可选价格组,某些用户的产品价格特殊(降低)。 问题是 - 这样我在索引页面中列出的每个产品都会收到两个查询:
SELECT MIN("prices"."value") FROM "prices" WHERE "prices"."product_id" = $1 AND "prices"."price_group_id" IN (1, 2) [["product_id", 3]]
和
SELECT "prices".* FROM "prices" WHERE "prices"."product_id" = $1 AND "prices"."price_group_id" = $2 ORDER BY "prices"."id" ASC LIMIT 1 [["product_id", 3], ["price_group_id", 1]]
所以,这是我的问题: 是否有一些(简单)方法一次加载所有内容?就像获取具有默认和最低价格字段的产品对象列表一样。 我理解如何在单个SQL查询中完成它,但我想不出任何更自然的东西,rails-activerecord-way。
****更新: 我最终得到了
# I'm using Kaminari pagination
@products = Product.page(params[:page]).per(6)
product_ids = @products.map(&:id)
@best_prices=Price.where(product_id: product_ids, price_group_id: @user_price_groups).group(:product_id).minimum(:value)
@default_prices=Price.where(product_id: product_ids, price_group_id: 1).group(:product_id).minimum(:value)
这些群组查询会产生类似{ product_id => value }, ...
的哈希值,所以我只需要在我的视图中使用@best_prices[product.id]
等等。
感谢大家!
答案 0 :(得分:1)
我同意@Frederick Cheung,我不喜欢自定义的SQL查询,而且我不熟悉arel,所以我的解决方案将是:
products = Product.limit(10)
product_ids = products.map(&:id)
products_hash = products.reduce({}) do |hash, product|
hash[price.id] = {product: product} }
hash
end
best_prices = Price.select("MIN(value) AS min_price, product_id")
.where(product_id: product_ids, price_group_id: price_groups)
.group("product_id")
.reduce({}) do |hash, price|
hash[price.product_id] = { best_price: price.min_price }
hash
end
default_prices = Price.where(price_group_id: 1, product_id: product_ids)
.reduce({}) do |hash, price|
hash[price.product_id] = {default_price: price.value }
hash
end
# A hash like {
# 1: {product: <Product ...>, best_price: 12, default_price: 11},
# 2: {product: <Product ...>, best_price: 12, default_price: 11},
# 3: {product: <Product ...>, best_price: 12, default_price: 11}
# }
result = products_hash.deep_merge(best_prices).deep_merge(default_prices)
仍然需要三个任务,而不仅仅是一个,但这解决了N + 1问题。
答案 1 :(得分:0)
只需添加默认范围并包含价格即可解决您的问题
def func():
return 'hello'
print eval('func()')
print globals()["func"]()
print locals()["func"]()
>>> hello
>>> hello
>>> hello