Rails - 使用数据库查询加速我的项目#索引操作



我注意到,随着我一直在开发我的应用程序并创建大量项目/列表,加载时间变得越来越长。我怀疑这是我在控制器中的操作查询数据库的方式。该视图使用 erb 执行 @item.each do 并检查是否@item.orders.any?并且将仅显示没有订单的项目。代码基本上必须遍历数据库中的每个项目执行此检查,然后呈现结果,我怀疑这将是超过 100+ 项的噩梦。

有没有更聪明的方法可以做到这一点?

这是一个简单的市场应用程序,买家和卖家可以在其中开展业务。卖家列出了一个项目,它与其他项目一起显示在"项目#索引"页面上。买家点击它并单击购买,这将他们带到一个订单页面,然后他们可以PayPal结账。收到 IPN 后PayPal订单将提交到数据库。

这是代码:

加载索引操作时服务器日志的摘录

Started GET "/" for 73.110.34.200 at 2017-07-31 13:19:38 -0500
Cannot render console from 73.110.34.200! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by ItemsController#index as */*
Rendering items/index.html.erb within layouts/application
Item Load (10.3ms)  SELECT "items".* FROM "items" ORDER BY "items"."created_at" DESC, created_at DESC
Order Exists (1.7ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 19], ["LIMIT", 1]]
Order Exists (3.2ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 18], ["LIMIT", 1]]
Order Exists (0.4ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 17], ["LIMIT", 1]]
Order Exists (0.7ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 16], ["LIMIT", 1]]
Order Exists (0.6ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 15], ["LIMIT", 1]]
Order Exists (0.6ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 14], ["LIMIT", 1]]
Order Exists (0.8ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 13], ["LIMIT", 1]]
Order Exists (0.6ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 12], ["LIMIT", 1]]
Order Exists (0.8ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 11], ["LIMIT", 1]]
Order Exists (0.4ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 10], ["LIMIT", 1]]
Order Exists (0.8ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 9], ["LIMIT", 1]]
Order Exists (3.7ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 8], ["LIMIT", 1]]
Order Exists (0.5ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 7], ["LIMIT", 1]]
Order Exists (0.5ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 6], ["LIMIT", 1]]
Order Exists (0.6ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 5], ["LIMIT", 1]]
Order Exists (0.4ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 4], ["LIMIT", 1]]
Order Exists (0.5ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 3], ["LIMIT", 1]]
Order Exists (0.4ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 2], ["LIMIT", 1]]
Order Exists (0.8ms)  SELECT  1 AS one FROM "orders" WHERE "orders"."item_id" = $1 LIMIT $2  [["item_id", 1], ["LIMIT", 1]]
Rendered items/index.html.erb within layouts/application (226.2ms)
Rendered layouts/_rails_default.html.erb (194.0ms)
Rendered layouts/_shim.html.erb (0.5ms)
Rendered layouts/_header.html.erb (20.0ms)
Rendered layouts/_messages.html.erb (1.1ms)
Completed 200 OK in 666ms (Views: 564.1ms | ActiveRecord: 28.3ms)

项控制器

class ItemsController < ApplicationController
before_action :set_item, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
rescue_from ActiveRecord::RecordNotFound, with: :deny_access
rescue_from ActionView::MissingTemplate, with: :template_not_found

def garage
@items = current_user.items
end
def show
@item = Item.find(params[:id])
end
# GET /items
# GET /items.json
def index
@items = Item.all
if params[:search]
@items = Item.search(params[:search]).order("created_at DESC")
else
@items = Item.all.order("created_at DESC")
end
end
# GET /items/new
def new
@item = current_user.items.build
end
# GET /items/1/edit
def edit
end
# POST /items
# POST /items.json
def create
@item = current_user.items.build(item_params)
@item.username = current_user.username
respond_to do |format|
if @item.save
format.html { redirect_to @item, notice: 'Item was successfully created.' }
format.json { render :show, status: :created, location: @item }
else
format.html { render :new }
format.json { render json: @item.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /items/1
# PATCH/PUT /items/1.json
def update
respond_to do |format|
if @item.update(item_params)
format.html { redirect_to @item, notice: 'Item was successfully updated.' }
format.json { render :show, status: :ok, location: @item }
else
format.html { render :edit }
format.json { render json: @item.errors, status: :unprocessable_entity }
end
end
end
# DELETE /items/1
# DELETE /items/1.json
def destroy
@item.destroy
respond_to do |format|
format.html { redirect_to items_url, notice: 'Item was successfully destroyed.' }
format.json { head :no_content }
end
end
def favorite
@item = Item.find(params[:id])
type = params[:type]
if type == "favorite"
current_user.favorites << @item
redirect_to :back
elsif type == "unfavorite"
current_user.favorites.delete(@item)
redirect_to :back
else
redirect_to :back
end
end
def favorites
@items = current_user.favorites
end
def deny_access
redirect_to :back
rescue ActionController::RedirectBackError
redirect_to root_path
end
def template_not_found
redirect_to :back
rescue ActionView::MissingTemplate
redirect_to root_path
end
private
# Use callbacks to share common setup or constraints between actions.
def set_item
@item = Item.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def item_params
params.require(:item).permit(:title, :content, :filepicker_url, :price, :shipping_price, :paypal_email)
end
end

项目#索引视图

<div class="row">
<%= form_tag(items_path, :method => "get") do %>
<div class="col-lg-6 col-lg-offset-3">
<div class="input-group">
<%= text_field_tag :search, params[:search], placeholder: "Search items...", class: "form-control" %>
<div class="input-group-btn">
<%= submit_tag "Search", :name => nil, class: "btn btn-default btn-block" %>
</div>
</div>
</div>
<% end %>
</div>
<br>
<% if @items.blank? %>
<h4>There are no items containing the term <%= params[:search] %>.</h4>
<% end %>
<h1>Items</h1>
<% @items.each do |item| %>
<% if item.orders.any? %>
<% else %>
<div class="col-md-3">
<%= link_to(filepicker_image_tag(item.filepicker_url.split(",").last, w: 200, h: 200, fit: 'crop'), item) %><br>
<p class="bold"><%= item.title %><br></p>
<p><%= link_to number_to_currency(item.price), item %></p>
<p class="small"><%= time_ago_in_words(item.created_at) %> ago</p>
</div> 
<% end %>
<% end %>
<br>

谢谢你的时间!

尝试使用includes预先加载关联。

class ItemsController < ApplicationController
def index
if params[:search]
@items = Item.search(params[:search]).includes(:orders).order("created_at DESC")
else
@items = Item.all.includes(:orders).order("created_at DESC")
end
end
end

最新更新