【问题标题】:How can I display the first and last child record for a parent row in Rails 3.1?如何在 Rails 3.1 中显示父行的第一个和最后一个子记录?
【发布时间】:2012-01-02 23:21:17
【问题描述】:

我对 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       |
--------------------------------------------------------

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

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

解决方案:2012 年 1 月 3 日

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

  • From PitStop 记录的 stop_number 值始终为 0。
  • To PitStop 记录的 stop_number 值始终为 99。

这是有道理的,因为第一个 PitStop 根本不是真正的“进站”,它是您开始的地方(即 0)。最后一个 PitStop 也不是真正的“进站”,它是您的终点位置(即 99)。当然,这是假设任何路线都不会有超过 98 个“进站”。 :) 对我来说是安全的假设......

这些逻辑定义的“stop_numbers”还解决了我查询所有子 PitStop 以显示“行程/索引”的问题,并且我可以在每次行程中快速有效地查询这些值(不再需要对 .first 或 @987654326 进行子查询@):

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

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

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 的提示!

【问题讨论】:

    标签: activerecord ruby-on-rails-3.1


    【解决方案1】:

    向 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
    

    对于开始 put_stop,您可以设置 == 1,对于最后一个,您需要在 trips 表中添加一个额外的列,用于缓存最后的行程编号。您还可以在父节点上缓存这两个pit_stops 的ID。 这样您就可以进行预加载。

    【讨论】:

      【解决方案2】:

      好吧,如果可以在两次 db 行程中执行此操作,您可以使用

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

      【讨论】:

      • 谢谢。我现在看到这与@Durrant 的建议非常相似,但更具表现力。我很欣赏这种努力,但是我担心这也需要我查询所有的pit_stops 列表,然后检索第一个和最后一个(通过颠倒顺序并获得第一个)。 :) 感谢您的想法...再次,我正在寻找一种方法来查询单个查询中的第一个和最后一个。还有其他想法吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-27
      • 2016-04-16
      • 1970-01-01
      相关资源
      最近更新 更多