【问题标题】:Optimizing purchase querying in Rails在 Rails 中优化购买查询
【发布时间】:2012-02-03 15:56:45
【问题描述】:

我正在使用 Rails 实现一个在线应用商店。其数据模型如下:

class User < ActiveRecord::Base
  has_many purchase_records
  has_many items, :through => purchase_records
end

class Item < ActiveRecord::Base
  has_many purchase_records
  has_many users, :through => purchase_records
end

class PurchaseRecord < ActiveRecord::Base
  belongs_to :user
  belongs_to :item
end

它有一个页面显示可用项目和价格,如果用户购买了该项目,价格将是一个下载链接,就像 App Store 一样。编写了一个视图助手来帮助生成此类链接:

def download_link(item)
  # generate a download link
end

def item_link(item)
  if current_user and current_user.items.where(:id => item.id).first != nil
    # User already purchased it
    download_link(item, 'book-price')
  else
    # Not purchased yet, show price and link to its details
    link_to item.price, item
  end
end

current_user 由设计定义。它工作得很好,除了它需要为一个包含 20 个项目的页面花费 20 个额外的数据库查询,因为它需要检查用户是否为每一个项目购买了该项目。我想知道是否可以对其进行优化,例如预加载当前用户购买的物品,但我不知道如何在视图助手中编写它。

【问题讨论】:

    标签: ruby-on-rails database ruby-on-rails-3 activerecord


    【解决方案1】:

    我刚刚为客户实现了可下载内容。

    我所做的是在用户类上编写一个实例方法来检索用户购买的物品,例如:

    class User < ActiveRecord::Base
      ...
      def downloads
        self.orders.collect { |o| o.products }.flatten
      end
    end
    

    你可以使用包含吗?检查用户是否购买商品的方法,例如:

    def item_link(item)
      if current_user && current_user.downloads.include?(item)
        download_link(item, 'book-price')
      else
        link_to item.price, item
      end
    end    
    

    不幸的是,虽然这更明确一点,但每次点击 item_link 时,它仍然会循环遍历用户的订单。我建议使用 Rails 低级缓存对此进行优化,您可以在每次用户登录或完成购买时清除缓存。

    Rails 低级缓存可能如下所示:

    def downoads
      Rails.cache.fetch("user-downloads-#{self.id}") do
        self.orders.collect { |o| o.products }.flatten
      end
    end
    

    并调用以下命令清除缓存:

    Rails.cache.delete("user-downloads-#{self.id}")
    

    【讨论】:

      【解决方案2】:

      您可以将用户购买的商品设置为控制器中的实例变量。那么你只需要访问数据库一次:

      # app/controllers/items_controller.rb
      def index
        @purchased_items = current_user.items
      end
      
      # app/helpers/items_helper.rb
      def item_link(item)
        if @purchased_items.include?(item)
          download_link(...)
        else
          link_to ...
        end
      end
      

      【讨论】:

        【解决方案3】:

        好吧,你不要在视图助手中写它。您在名为 purchased_items 的用户模型上创建了一个范围,您可以在其中检查用户购买的所有商品。

        由于您没有提供 User、Item 以及它们之间的任何关系的源代码,我只能给您大致的提示。

        【讨论】:

        • 感谢您的建议。我已经在问题中添加了数据模型。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-09-12
        • 2013-02-18
        • 1970-01-01
        • 2021-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多