Ruby on rails 如何使用第二个表中的列值(如果存在)覆盖表列值?
我正在构建一个电子商务应用程序,您可以在其中为产品设置特定于用户的价格。如果未为特定用户设置特定产品的价格,则会显示默认产品价格 它工作得很好,但我正在寻找更有效的解决方案,因为我对当前的解决方案不满意 表格:Ruby on rails 如何使用第二个表中的列值(如果存在)覆盖表列值?,ruby-on-rails,Ruby On Rails,我正在构建一个电子商务应用程序,您可以在其中为产品设置特定于用户的价格。如果未为特定用户设置特定产品的价格,则会显示默认产品价格 它工作得很好,但我正在寻找更有效的解决方案,因为我对当前的解决方案不满意 表格: @products = Product.with_user_prices(current_user) <% @products.each do |product| %> <%= product.price_for_user %> <% end %>
@products = Product.with_user_prices(current_user)
<% @products.each do |product| %>
<%= product.price_for_user %>
<% end %>
# will perform LEFT OUTER JOIN (to eager load both `prices` and `prices -> user`) preventing N+1 queries
@products = Product.eager_load(prices: :user)
<% @products.each do |product| %>
<%= product.price_for_user(current_user) %>
<% end %>
- 使用者
- 产品(所有产品信息+常规价格)
- 价格(用户id、产品id、用户价格)
@products = Product.with_user_prices(current_user)
<% @products.each do |product| %>
<%= product.price_for_user %>
<% end %>
# will perform LEFT OUTER JOIN (to eager load both `prices` and `prices -> user`) preventing N+1 queries
@products = Product.eager_load(prices: :user)
<% @products.each do |product| %>
<%= product.price_for_user(current_user) %>
<% end %>
class用户
类产品
class Price
如何在controller中获得所有具有用户特定价格的产品:
@products = Product.with_user_prices(current_user)
<% @products.each do |product| %>
<%= product.price_for_user %>
<% end %>
# will perform LEFT OUTER JOIN (to eager load both `prices` and `prices -> user`) preventing N+1 queries
@products = Product.eager_load(prices: :user)
<% @products.each do |product| %>
<%= product.price_for_user(current_user) %>
<% end %>
@products=产品。具有用户价格(当前用户)
如何在视图中显示它们:
@products = Product.with_user_prices(current_user)
<% @products.each do |product| %>
<%= product.price_for_user %>
<% end %>
# will perform LEFT OUTER JOIN (to eager load both `prices` and `prices -> user`) preventing N+1 queries
@products = Product.eager_load(prices: :user)
<% @products.each do |product| %>
<%= product.price_for_user(current_user) %>
<% end %>
正如您所看到的,我目前正在加入价格表,然后在视图中显示用户价格(价格表)(如果存在),否则显示常规价格(产品表)
我希望在一个查询中解决所有问题,根据当前用户,只保留一个具有适当值的价格列新答案:
请不要将此答案标记为正确答案,因为我基本上只是将Marek的代码和您的代码扩展到我的代码中,因为经过几次尝试,我得出了您已经完成的操作,但我只是将其放在这里,以防它对任何人都有帮助:
app/models/product.rb
class Product < ApplicationRecord
def self.with_user_prices(user)
joins(
sanitize_sql_array([
"LEFT OUTER JOIN prices on prices.product_id = products.id AND prices.user_id = ?", user.id
])
).select(
'products.*',
'COALESCE(prices.user_price, products.regular_price) AS price_for_user'
)
end
end
class Product < ApplicationRecord
has_many :prices
def price_for_user(user)
prices.includes(:user).where(
users: { id: user.id }
).first&.user_price || regular_price
end
end
查看:
@products = Product.with_user_prices(current_user)
<% @products.each do |product| %>
<%= product.price_for_user %>
<% end %>
# will perform LEFT OUTER JOIN (to eager load both `prices` and `prices -> user`) preventing N+1 queries
@products = Product.eager_load(prices: :user)
<% @products.each do |product| %>
<%= product.price_for_user(current_user) %>
<% end %>
查看:
@products = Product.with_user_prices(current_user)
<% @products.each do |product| %>
<%= product.price_for_user %>
<% end %>
# will perform LEFT OUTER JOIN (to eager load both `prices` and `prices -> user`) preventing N+1 queries
@products = Product.eager_load(prices: :user)
<% @products.each do |product| %>
<%= product.price_for_user(current_user) %>
<% end %>
您可以使用以下功能:
class Product < ApplicationRecord
# ...
def self.with_user_prices(user)
includes(:prices).where(prices: { user_id: user.id }).select(
'products.*, COALESCE(prices.user_price, products.regular_price) as price'
)
end
end
类产品
然后,您可以通过以下方式使用它:
<%= product.price %>
请注意,我简化了
产品。通过使用includes
稍微简化了\u user\u prices
方法,该方法将生成SQL左连接
查询,因为存在价格
的条件 从技术上讲,Product.includes(prices::user)
默认情况下不会生成left join
,而是生成三个单独的DB查询。此外,它末尾的all
也是不必要的。另外,Product#price_for_user
也不起作用,因为在Product
实例上没有定义方法包含。@MarekLipka hi yes。谢谢你完全正确。我现在正在更新它,改为使用eager\u load
。此外,您正在对每个产品执行单独的数据库查询:价格。包括(:user)。其中(…)
,所以这里有N+1问题。@MarekLipka^我还不确定它是否会对每个产品执行SQL,因为我急切地加载了它。但是,正如你所说,我可能错了。我将在本地rails应用程序上试用它,并相应地调整它。谢谢:)谢谢!我感谢你的努力。它工作正常。这绝对是对我的解决方案的一个很好的升级,朝着我想要的方向发展。我相信@MarekLipka会完善它:)你的数据库引擎是什么?@MarekLipka目前我使用的是开发SQLite3数据库,那么我的答案应该是有效的。如果用户没有任何自定义价格设置,它将返回零,否则:未定义的方法“价格”#