Ruby on Rails中更好的递归循环

时间:2013-03-26 16:59:38

标签: ruby-on-rails ruby

使用Rails 3.2。假设我想要2个选项:

  1. 获取所有旅行照片。
  2. 获取第一张旅行照片。
  3. 我有以下代码:

    # trip.rb
    class Trip < ActiveRecord::Base
      has_many :trip_days
    
      def trip_photos
        if (photos = trip_days.map(&:spots).flatten.map(&:photos).flatten.map)
          photos.each do |photo|
            photo.url(:picture_preview)
          end
        end 
      end
    
      def trip_photo
        trip_photos.first
      end
    end
    
    # trip_day.rb
    class TripDay < ActiveRecord::Base
      belongs_to :trip
      has_many :trip_day_spots
      has_many :spots, :through => :trip_day_spots
    end
    
    # trip_day_spot.rb
    class TripDaySpot < ActiveRecord::Base
      belongs_to :trip_day
      belongs_to :spot
    end
    
    #spot.rb
    class Spot < ActiveRecord::Base
    end
    
    # trips_controller.rb
    class TripsController < ApplicationController
      def index    
        @trips = Trip.public.paginate(:page => params[:page], :per_page => 25)
      end
    end
    

    正如预期的那样,trip_photos方法会生成大量SQL查询。我想知道是否有更好的方法可以做到这一点?

3 个答案:

答案 0 :(得分:0)

这是因为N + 1个查询。在这种情况下,我们需要急切加载基础对象的所有关联,这样当你调用它的关联对象时,它不会触发任何查询来获取它们,只是它将从它的缓存对象中获取它们。

希望这会有效,但未经过测试。我假设并写了以下查询。

def trip_photos
  user_trip_days = trip_days.includes(:spots => :photos)
  photos = user_trip_days.collect {|trip_day| trip_day.spots.map(&:photos).flatten}.flatten
  photos.each do |photo|
    photo.url(:picture_preview)
  end if photos
end

如果您有任何错误,请告诉我。

有关在ActiveRecord中加载关联对象的更多信息,请完成

Guides for RailsRails cast以及Rails Tips

答案 1 :(得分:0)

这可能不是最好的方式,但如果你真的想要在一次击中中获得所有的位置,你可以做类似的事情:

def spots
 Spot.joins("join trip_days_spots on spots.id = trip_days_spots.spot_id join trip_days on trip_days.id = trip_days_spots.trip_day_id join trips on trips.id = trip_days.trip_id").where("trips.id = ?", self.id)
end

然后将你的循环改为:

def trip_photos
  spots.map(&:photos).flatten.each do |photo|
    photo.url(:picture_preview)
  end
end

答案 2 :(得分:0)

代码工作正常,但要急切加载,只需添加:include

# trips_controller.rb
class TripsController < ApplicationController
  def index    
    @trips = Trip.public.paginate(:include => [:trip_days => [:spots => :photos]], :page => params[:page], :per_page => 25)
  end
end