Я изучаю Rails и работаю над образцом приложения, чтобы отслеживать рецепты пива.
У меня есть модель под названием Рецепт, которая содержит название рецепта и эффективность.
У меня есть модель под названием Ingredient, в которой используется STI - она подразделяется на солод, хмель и дрожжи.
Наконец, чтобы связать рецепты и ингредиенты, я использую объединенную таблицу rec_items, которая содержит recipe_id, ингридиент_id и информацию, относящуюся к этой комбинации рецепт / ингредиент, такую как количество и время кипячения.
Кажется, все работает хорошо - я могу найти все свои солоды с помощью Malt.all и все ингредиенты с помощью Ingredient.all. Я могу найти ингредиенты рецепта, используя @ recipe.ingredients и т. Д.
Однако сейчас я работаю над представлением рецепта и не понимаю, как лучше всего выполнить следующее:
Я хочу отобразить название рецепта и связанную с ним информацию, а затем перечислить ингредиенты, но разделить их по типу. Итак, если у меня есть черный IPA с эффективностью 85% и в нем 5 сортов солода и 3 сорта хмеля, результат будет примерно таким:
BLACK IPA (85%)
Ingredient List
MALTS:
malt 1
malt 2
...
HOPS:
hop 1
...
Теперь я могу вытащить @ recipe.rec_items и перебирать их, тестируя каждый rec_item.ingredient для type == "Malt", а затем проделывать то же самое для хмеля, но это не кажется очень эффективным для Rails и неэффективным. Итак, как лучше всего это сделать? Я могу использовать @ recipe.ingredients.all для извлечения всех ингредиентов, но не могу использовать @ recipe.malts.all или @ recipe.hops.all для извлечения только этих типов.
Есть ли другой синтаксис, который мне следует использовать? Стоит ли использовать @ recipe.ingredient.find_by_type ("Солод")? Делать это в контроллере и передавать коллекцию в представление или делать это прямо в представлении? Нужно ли мне также указывать отношение has_many в моих моделях хмеля и солода?
Я могу заставить его работать так, как я хочу, используя условные операторы или find_by_type, но я делаю акцент на том, чтобы сделать это «способом Rails» с минимальными накладными расходами БД.
Спасибо за помощь!
Текущий базовый код:
Recipe.rb
class Recipe < ActiveRecord::Base
has_many :rec_items
has_many :ingredients, :through => :rec_items
end
Ingredient.rb
class Ingredient < ActiveRecord::Base
has_many :rec_items
has_many :recipes, :through => :rec_items
end
Солод.rb
class Malt < Ingredient
end
Hop.rb
class Hop < Ingredient
end
RecItem.rb
class RecItem < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
end
Recipes_controller.rb
class RecipesController < ApplicationController
def show
@recipe = Recipe.find(params[:id])
end
def index
@recipes = Recipe.all
end
end
Обновлено для добавления
Теперь я не могу получить доступ к атрибутам таблицы соединений, поэтому я опубликовал новый вопрос:
Rails - использование group_by и has_many: through и попытка доступа к атрибутам таблицы соединений
Если кто-то может помочь с этим, я был бы признателен!
3 ответа
Я давно не употреблял ИППП, раз или два обгорел. Так что, возможно, я пропущу некоторые STI-фу, которые упростят это. Тем не менее ...
Есть много способов сделать это. Во-первых, вы можете сделать объем для каждого из солода, хмеля и дрожжей.
class Ingredient < ActiveRecord::Base
has_many :rec_items
has_many :recipes, :through => :rec_items
named_scope :malt, :conditions => {:type => 'Malt'}
named_scope :hops, :conditions => {:type => 'Hops'}
...
end
Это позволит вам сделать что-то в строке:
malts = @recipe.ingredients.malt
hops = @recipe.ingedients.hops
Хотя это удобно, но с точки зрения базы данных это не самый эффективный вариант. Чтобы получить все три типа, нам нужно было бы выполнить три запроса.
Так что, если мы не говорим о тоннах ингредиентов в рецепте, вероятно, будет лучше просто добавить все @ recipe.ingredients, а затем сгруппировать их примерно так:
ingredients = @recipe.ingredients.group_by(&:type)
Это выполнит один запрос, а затем сгруппирует их в хэш в рубиновой памяти. Хеш будет отключен от типа и будет выглядеть примерно так:
{"Malt" => [first_malt, second_malt],
"Hops" => [first_hops],
"Yeast" => [etc]
}
Затем вы можете ссылаться на эту коллекцию, чтобы отображать элементы, как хотите.
ingredients["Malt"].each {|malt| malt.foo }
Вы можете использовать group_by
здесь.
recipe.ingredients.group_by {|i| i.type}.each do |type, ingredients|
puts type
ingredients.each do |ingredient|
puts ingredient.inspect
end
end
Полезность STI в данном случае сомнительна. Возможно, вам будет лучше с прямой категоризацией:
class Ingredient < ActiveRecord::Base
belongs_to :ingredient_type
has_many :rec_items
has_many :recipes, :through => :rec_items
end
IngredientType определяет ваши различные типы и с этого момента становится числовой константой.
Когда вы пытаетесь отобразить список, это становится проще. Обычно я предпочитаю вытаскивать промежуточные записи напрямую, а затем присоединяться по мере необходимости:
RecItem.sort('recipe_id, ingredient_type_id').includes(:recipe, :ingredient).all
Что-то подобное дает вам гибкость для сортировки и группировки по мере необходимости. Вы можете настроить условия сортировки, чтобы получить правильный порядок. Это также может работать с STI, если вы сортируете по столбцу типа.
Похожие вопросы
Связанные вопросы
Новые вопросы
ruby-on-rails
Ruby on Rails - это полнофункциональная платформа веб-приложений с открытым исходным кодом, написанная на Ruby. Он следует популярной модели фреймворка MVC и известен своим подходом «соглашение поверх конфигурации» при разработке приложений.