通过子属性检索has_many关联作为哈希

时间:2011-09-21 13:03:15

标签: ruby-on-rails activerecord

是否可以根据其中一个属性将has_many关联中的子元素检索到哈希中?

例如,想象一下每天都有一道菜的菜单:

class Menu
  has_many :dishes
end

class Dish
  belongs_to :menu
end

Dish有一个密钥day,它是mondaytuesday等之一。是否有某种方法可以设置has_many关联这样Menu.dishes会返回类似于{:monday => 'spaghetti', :tuesday => 'tofu', ... }的哈希值?

2 个答案:

答案 0 :(得分:5)

不确定。这样的东西应该足够了(假设例如“意大利面条”存储在名为food的列中。)

class Dish
  belongs_to :menu

  scope :by_day { select [ :day, :food ] }

  def self.by_day_hash
    by_day.all.reduce({}) {|hsh,dish| hsh[dish.day] = dish.food; hsh }
  end
end

class Menu
  has_many :dishes

  def dishes_by_day
    dishes.by_day_hash
  end
end

# Usage
m = Menu.where( ... )
m.dishes_by_day #=> { "monday" => "Spaghetti", "tuesday" => "Tofu" }

所以这里发生的事情是Dish by_day范围只返回两列,dayfood。但是,它仍然返回Dish个对象而不是哈希(因为这是范围的作用),因此我们定义了一个类方法by_day_hash,它接受​​该结果并将其转换为哈希。

然后在Menu中我们定义了dishes_by_day,它只调用我们在关联上创建的方法。您可以调用此dishes,但我认为最好将该名称保留为原始关联,因为您可能希望稍后将其用于其他内容。

顺便提一下可选以下内容,如果您的眼睛已经眩晕,请立即跳过),我可能会定义by_day_hash这样:

class Dish
  belongs_to :menu

  scope :by_day { select [ :day, :hash ] }

  def to_s
    food
  end

  def by_day_hash
    hsh = HashWithIndifferentAccess.new
    by_day.reduce(hsh) {|hsh, dish| hsh[dish.day] = dish }
  end
end

# Usage
m = Menu.where( ... )
m.dishes_by_day #=> { "monday" => #<Dish food: "Spaghetti", ...>, "tuesday" => #<Dish food: "Tofu", ...>, ... }

...这样,当您拨打电话时,您仍会获得完整的Dish个对象by_day_hash["monday"]to_s方法意味着您可以将其放入<%= @menu.dishes_by_day["monday"] %>之类的视图中,然后获取“Spaghetti”而不是#<Dish day: "monday", food: "Spaghetti">

最后,您可能还会注意到我使用了HashWithIndifferentAccess.new而不是{}(哈希)。 HashWithIndifferentAccess是一个由Rails提供(并在任何地方使用)的类,它与Hash相同但允许你做任何一个例如some_hash["monday"]some_hash[:monday]并获得相同的结果。完全可选,但非常方便。

答案 1 :(得分:0)

在现代的ActiveRecords和Rubys中,这是一个很好的方法:

# In class Menu
def foods_by_day
  Hash[ dishes.pluck(:day, :food) ]
end

dishes.pluck(:day, :food)返回类似[ ['monday', 'spaghetti'], ['tuesday', 'tofu], ... ]的数组; Hash[]将该数组数组转换为哈希值。