Получить ассоциацию has_many как хэш по свойству child - PullRequest
1 голос
/ 21 сентября 2011

Можно ли извлечь дочерние элементы в ассоциации has_many в хеш на основе одного из их свойств?

Например, представьте себе меню, в котором есть одно блюдо на каждый день:

class Menu
  has_many :dishes
end

и

class Dish
  belongs_to :menu
end

Dish имеет ключ, day, который является одним из monday, tuesday и т. Д. Есть ли какой-либо способ установить ассоциацию has_many таким образом, чтобы Menu.dishes возвращал хэш, аналогичный {:monday => 'spaghetti', :tuesday => 'tofu', ... }

Ответы [ 2 ]

5 голосов
/ 21 сентября 2011

Конечно.Что-то вроде этого должно быть достаточно (при условии, что, например, «спагетти» хранится в столбце с именем 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 возвращает только два столбца, day и food.Однако он по-прежнему возвращает 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"] %> и получить «Спагетти» вместо #<Dish day: "monday", food: "Spaghetti">.

Наконец, вы также можетеобратите внимание, я использовал HashWithIndifferentAccess.new вместо {} (хэш).HashWithIndifferentAccess - это класс, предоставляемый (и используемый везде) Rails, который идентичен Hash, но позволяет вам делать, например, some_hash["monday"] или some_hash[:monday] и получать тот же результат.Абсолютно необязательно, но очень удобно.

0 голосов
/ 13 сентября 2015

Вот хороший способ сделать это в современных ActiveRecords и Rubys:

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

dishes.pluck(:day, :food) возвращает массив типа [ ['monday', 'spaghetti'], ['tuesday', 'tofu], ... ]; Hash[] преобразует этот массив массивов в хеш.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...