如何在Rails 3.1中显示父行的第一个和最后一个子记录?

时间:2012-01-02 23:21:17

标签: activerecord ruby-on-rails-3.1

我对Rails很新,只是喜欢它富有表现力的建模架构。我目前正在构建一个路由系统,在两个表之间有一对多的链接。

考虑以下架构:

class CreateTrips < ActiveRecord::Migration
  def self.up
    create_table :trips do |t|
      t.string :name
    end
  end
  ...
end


class CreatePitStop < ActiveRecord::Migration
  def self.up
    create_table :pit_stops do |t|
      t.references :trip
      t.integer :stop_number
      t.string :location
    end
  end
  ...
end

现在考虑一次旅行的以下数据:

t = Trips.create!({ :name => "Christmas Visits Route" })
t.pit_stops.create!({ :stop_number => 1, :location => "Home" })
t.pit_stops.create!({ :stop_number => 2, :location => "Mom and Dads" })
t.pit_stops.create!({ :stop_number => 3, :location => "In-Laws" })
t.pit_stops.create!({ :stop_number => 4, :location => "Grandma's" })
t.pit_stops.create!({ :stop_number => 5, :location => "Time Square" })

我想要做的是在“行程/索引”视图的单个“行程”行中显示所有行程名称的列表以及第一个和最后一个PitStop的位置。我不在乎两者之间的进站,我只是想知道我的起点和结束地点:

--------
My Trips
--------

--------------------------------------------------------
| Route Name              | From   | To                |
--------------------------------------------------------
| Christmas Visits Route  | Home   | Time Square       |
--------------------------------------------------------

我想从查询中删除所有“中间”PitStops,因为每次旅行会有很多。

问题:使用ActiveRecord从数据库中提取此数据的最佳方法是什么?

解决方案:2012年1月3日

我通过将PitStop逻辑应用于Trip模型解决了我的问题。我的From和To PitStop现在有硬编码的stop_number值。在完成它之后,我发现解决方案实际上对现实世界也有意义(双赢)。

  • 发件人 PitStop记录的stop_number值始终为0。
  • PitStop记录的stop_number值始终为99.

这是有道理的,因为第一个PitStop根本不是一个“进站点”,它是你从哪里开始的(即0)。最后一个PitStop也不是一个“进站”,它是你的终点(即99)。当然,这假设任何路线都不会超过98次“进站”。 :)对我安全的假设...

这些逻辑定义的“stop_numbers”也解决了我查询所有子PitStops的“行程/索引”显示的问题,我可以快速有效地查询这些值,每次旅行(不再查询.first.last):

class Trip
  has_many :pit_stops
  has_one :starting_from, :class => 'PitStop', 
          :conditions => 'pit_stops.stop_number == 0'
  has_one :ending_at, :class => 'PitStop', 
          :conditions => 'pit_stops.stop_number == 99'

  def add_pit_stop(location)
    stop_num = @pit_stops.count - 1 # accounts for stop_number 99
    @pit_stops.create!({ :stop_number => stop_num, 
                         :location => location})
  end
end

如您所见,在定义 starting_from ending_at 记录(现在是逻辑业务规则)后,输入stop_numbers的技巧现在是一个简单的数学方程式)。现在我可以简单地说:

trip = create_trip("Christmas Visits", "Home", "Time Square")
trip.add_pit_stop("Mom and Dads")
trip.add_pit_stop("In-Laws")
trip.add_pit_stop("Grandma's")

我的(订购的)PitStop记录现在看起来像这样:

{ :stop_number => 0, :location => "Home" }
{ :stop_number => 1, :location => "Mom and Dads" }
{ :stop_number => 2, :location => "In-Laws" }
{ :stop_number => 3, :location => "Grandma's" }
{ :stop_number => 99, :location => "Time Square" }

在我看来非常优雅。感谢@clyfe提示!

2 个答案:

答案 0 :(得分:3)

在Trip类中添加两个额外的关系:

clss Trip
  has_many :pit_stops
  has_one :start_pit_stop, :class => 'PitStop', 
    :conditions => 'pit_stops.order_number == 1'
  has_one :end_pit_stop, :class => 'PitStop', 
    :conditions => 'pit_stops.order_number == trips.last_order_number'
end

对于start put_stop,您可以在== 1上进行条件化,对于最后一个,您需要在trips表上添加一个额外的列,用于缓存最后一个行程号。您还可以在父级上缓存这两个pit_stops的ID。 通过这种方式,您可以进行急切的加载。

答案 1 :(得分:1)

好吧,如果可以在两次db旅行中完成,你可以使用

first_stop = t.pit_stops.order(:stop_number).first
last_stop = t.pit_stops.order(:stop_number).reverse_order.first